Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

* workaround: idea debugger doesn't stop in Inner classes that extends from ObjCObject #754

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.robovm.compiler.plugin.AbstractCompilerPlugin;
import org.robovm.compiler.plugin.CompilerPlugin;
import org.robovm.compiler.target.framework.FrameworkTarget;
import org.robovm.compiler.util.generic.SootClassUtils;
import org.robovm.compiler.util.generic.SootMethodType;
import soot.Body;
import soot.BooleanType;
Expand Down Expand Up @@ -87,6 +88,7 @@
import soot.tagkit.SignatureTag;
import soot.util.Chain;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -694,6 +696,7 @@ public void beforeClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder)
@Override
public void beforeLinker(Config config, Linker linker, Set<Clazz> classes) {
preloadClassesForFramework(config, linker, classes);
preloadObjCClassHosts(config, linker, classes);
}

private static <E> List<E> l(E head, List<E> tail) {
Expand Down Expand Up @@ -2029,6 +2032,49 @@ private void preloadClassesForFramework(Config config, Linker linker, Set<Clazz>
}
}

private void preloadObjCClassHosts(Config config, Linker linker, Set<Clazz> classes) {
// TODO: remove once resolved on Idea side
// affects only debug builds
// workaround for Idea Bug: https://youtrack.jetbrains.com/issue/IDEA-332794
// Idea debugger is not happy if inner class is being loaded before host ones and
// ignores breakpoints inside.
// preload if all ObjCClass's is happening in ObjCClass.java, static initializer
// workaround -- is to preload all hosts before loading ObjCClass
if (config.isDebug()) {
SootClass objCObjectClazz = classes.stream()
.filter( c -> c.getClassName().equals(OBJC_OBJECT))
.map(Clazz::getSootClass)
.findFirst()
.orElse(null);
Set<String> objClassHosts = new HashSet<>();
if (objCObjectClazz != null) {
// proceed if we link with ObjObject
// look for all ObjCClasses, and these are internal -- preload hosts as well
for (Clazz clazz : classes) {
SootClass sootClass = clazz.getSootClass();
// skip if doesn't extend ObjCClass
if (!SootClassUtils.isAssignableFrom(sootClass, objCObjectClazz))
continue;
// skip if not inner
String hostClassName = SootClassUtils.getEnclosingClassName(sootClass);
if (hostClassName == null)
hostClassName = SootClassUtils.getDeclaringClassName(sootClass);
if (hostClassName != null)
objClassHosts.add(hostClassName);
}

if (!objClassHosts.isEmpty()) {
try {
byte[] bytes = StringUtils.join(objClassHosts, ",").getBytes("UTF8");
linker.addRuntimeData(OBJC_CLASS + ".preloadClasses", bytes);
} catch (UnsupportedEncodingException e) {
config.getLogger().error("Failed to prepare ObjCClass' hosts preload list");
}
}
}
}
}

public static class MethodCompiler extends AbstractMethodCompiler {
// structure to expose CustomClasses to objc side
// check https://opensource.apple.com/source/objc4/objc4-437/runtime/objc-runtime-new.h for details
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.robovm.compiler.util.generic;

import soot.SootClass;
import soot.SootMethod;
import soot.SootResolver;
import soot.tagkit.EnclosingMethodTag;
import soot.tagkit.InnerClassTag;
import soot.tagkit.Tag;

public final class SootClassUtils {
private SootClassUtils() {
}

public static String getDeclaringClassName(SootClass sootClass) {
for (Tag tag : sootClass.getTags()) {
if (tag instanceof InnerClassTag) {
InnerClassTag icTag = (InnerClassTag) tag;
if (icTag.getInnerClass() != null && icTag.getOuterClass() != null) {
String innerName = icTag.getInnerClass().replace('/', '.');
if (sootClass.getName().equals(innerName)) {
return icTag.getOuterClass().replace('/', '.');
}
}
}
}
return null;
}

public static String getEnclosingClassName(SootClass sootClass) {
EnclosingMethodTag emTag = (EnclosingMethodTag) sootClass.getTag("EnclosingMethodTag");
if (emTag != null) {
return emTag.getEnclosingClass();
}
return null;
}

/**
* checks if sootClass can be assigned with subSootClass (e.g. if subSootClass extends sootClass)
*/
public static boolean isAssignableFrom(SootClass subSootClass, SootClass sootClass) {
if (sootClass.equals(subSootClass)) {
return true;
}

if (subSootClass.hasSuperclass()) {
return isAssignableFrom(subSootClass.getSuperclass(), sootClass);
}

return false;
}
}
26 changes: 26 additions & 0 deletions compiler/objc/src/main/java/org/robovm/objc/ObjCClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.robovm.objc;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -56,6 +57,31 @@ public final class ObjCClass extends ObjCObject {

static {
ObjCRuntime.bind(ObjCClass.class);

// TODO: remove once resolved on Idea side
// affects only debug builds
// workaround for Idea Bug: https://youtrack.jetbrains.com/issue/IDEA-332794
// there is code bellow loads all ObjCClass' and some of them might be Inner ones
// idea seems to be ignoring PREPARE class events in case Inner class is
// being loaded before the host one
byte[] data = VM.getRuntimeData(ObjCClass.class.getName() + ".preloadClasses");
if (data != null) {
String[] innerClassHosts;
try {
innerClassHosts = new String(data, "UTF8").split(",");
} catch (UnsupportedEncodingException e) {
innerClassHosts = new String[0];
}
for (String host : innerClassHosts) {
try {
// preload class.
Class.forName(host);
} catch (Throwable t) {
System.err.println("Failed to preload inner ObjCClass host class " + host + ": " + t.getMessage());
}
}
}

@SuppressWarnings("unchecked")
Class<? extends ObjCObject>[] classes = (Class<? extends ObjCObject>[])
VM.listClasses(ObjCObject.class, ClassLoader.getSystemClassLoader());
Expand Down
Loading