Adds support of Class.getDeclaringClass method

This commit is contained in:
konsoletyper 2014-02-23 23:17:30 +04:00
parent 9cdc099b1a
commit 3195879467
9 changed files with 76 additions and 4 deletions

View File

@ -24,6 +24,7 @@ import org.teavm.model.*;
*/ */
public class NewInstanceDependencySupport implements DependencyListener { public class NewInstanceDependencySupport implements DependencyListener {
private DependencyNode allClassesNode; private DependencyNode allClassesNode;
private DependencyStack newInstanceStack;
@Override @Override
public void started(DependencyChecker dependencyChecker) { public void started(DependencyChecker dependencyChecker) {
@ -36,19 +37,31 @@ public class NewInstanceDependencySupport implements DependencyListener {
if (cls.hasModifier(ElementModifier.ABSTRACT) || cls.hasModifier(ElementModifier.INTERFACE)) { if (cls.hasModifier(ElementModifier.ABSTRACT) || cls.hasModifier(ElementModifier.INTERFACE)) {
return; return;
} }
if (cls.getMethod(new MethodDescriptor("<init>", ValueType.VOID)) != null) { MethodReader method = cls.getMethod(new MethodDescriptor("<init>", ValueType.VOID));
if (method != null) {
allClassesNode.propagate(className); allClassesNode.propagate(className);
} }
} }
@Override @Override
public void methodAchieved(DependencyChecker dependencyChecker, MethodDependency method) { public void methodAchieved(final DependencyChecker dependencyChecker, MethodDependency method) {
MethodReader reader = method.getMethod(); MethodReader reader = method.getMethod();
if (reader.getOwnerName().equals("java.lang.Class") && reader.getName().equals("newInstance")) { if (reader.getOwnerName().equals("java.lang.Class") && reader.getName().equals("newInstance")) {
newInstanceStack = method.getStack();
allClassesNode.connect(method.getResult()); allClassesNode.connect(method.getResult());
method.getResult().addConsumer(new DependencyConsumer() {
@Override public void consume(String type) {
attachConstructor(dependencyChecker, type);
}
});
} }
} }
private void attachConstructor(DependencyChecker checker, String type) {
MethodReference ref = new MethodReference(type, new MethodDescriptor("<init>", ValueType.VOID));
checker.linkMethod(ref, newInstanceStack).use();
}
@Override @Override
public void fieldAchieved(DependencyChecker dependencyChecker, FieldDependency field) { public void fieldAchieved(DependencyChecker dependencyChecker, FieldDependency field) {
} }

View File

@ -47,6 +47,9 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
case "newInstance": case "newInstance":
generateNewInstance(context, writer); generateNewInstance(context, writer);
break; break;
case "getDeclaringClass":
generateGetDeclaringClass(context, writer);
break;
} }
} }
@ -141,8 +144,27 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
writer.append("$rt_throw(ex);").softNewLine(); writer.append("$rt_throw(ex);").softNewLine();
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
writer.append("var instance = new cls();").softNewLine(); writer.append("var instance = new cls();").softNewLine();
writer.append("ctor(instance)"); writer.append("ctor(instance);").softNewLine();
writer.append("return instance"); writer.append("return instance;").softNewLine();
}
private void generateGetDeclaringClass(GeneratorContext context, SourceWriter writer) throws IOException {
String self = context.getParameterName(0);
writer.append("if (!").appendClass("java.lang.Class").append(".$$owners$$) {").indent().softNewLine();
writer.appendClass("java.lang.Class").append(".$$owners$$ = true;").softNewLine();
for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName);
writer.appendClass(clsName).append(".$$owner$$ = ");
if (cls.getOwnerName() != null) {
writer.appendClass(cls.getOwnerName());
} else {
writer.append("null");
}
writer.append(";").softNewLine();
}
writer.outdent().append("}").softNewLine();
writer.append("var cls = " + self + ".$data;").softNewLine();
writer.append("return cls.$$owner$$ != null ? $rt_cls(cls.$$owner$$) : null;").softNewLine();
} }
@Override @Override
@ -154,6 +176,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
case "getSuperclass": case "getSuperclass":
case "getComponentType0": case "getComponentType0":
case "forNameImpl": case "forNameImpl":
case "getDeclaringClass":
graph.getResult().propagate("java.lang.Class"); graph.getResult().propagate("java.lang.Class");
break; break;
} }

View File

@ -134,4 +134,8 @@ public class TClass<T extends TObject> extends TObject {
@GeneratedBy(ClassNativeGenerator.class) @GeneratedBy(ClassNativeGenerator.class)
public native T newInstance() throws TInstantiationException, TIllegalAccessException; public native T newInstance() throws TInstantiationException, TIllegalAccessException;
@GeneratedBy(ClassNativeGenerator.class)
@PluggableDependency(ClassNativeGenerator.class)
public native TClass<?> getDeclaringClass();
} }

View File

@ -97,4 +97,12 @@ public class ClassTest {
assertEquals(TestObject.class, instance.getClass()); assertEquals(TestObject.class, instance.getClass());
assertEquals(1, ((TestObject)instance).getCounter()); assertEquals(1, ((TestObject)instance).getCounter());
} }
@Test
public void declaringClassFound() {
assertEquals(ClassTest.class, new A().getClass().getDeclaringClass());
}
private static class A {
}
} }

View File

@ -26,6 +26,7 @@ public class ClassHolder extends ElementHolder implements ClassReader {
private Set<String> interfaces = new HashSet<>(); private Set<String> interfaces = new HashSet<>();
private Map<MethodDescriptor, MethodHolder> methods = new HashMap<>(); private Map<MethodDescriptor, MethodHolder> methods = new HashMap<>();
private Map<String, FieldHolder> fields = new HashMap<>(); private Map<String, FieldHolder> fields = new HashMap<>();
private String ownerName;
public ClassHolder(String name) { public ClassHolder(String name) {
super(name); super(name);
@ -106,4 +107,13 @@ public class ClassHolder extends ElementHolder implements ClassReader {
fields.remove(field.getName()); fields.remove(field.getName());
field.setOwner(null); field.setOwner(null);
} }
@Override
public String getOwnerName() {
return ownerName;
}
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
} }

View File

@ -34,4 +34,6 @@ public interface ClassReader extends ElementReader {
FieldReader getField(String name); FieldReader getField(String name);
Collection<? extends FieldReader> getFields(); Collection<? extends FieldReader> getFields();
String getOwnerName();
} }

View File

@ -59,6 +59,7 @@ public class CopyClassHolderSource implements ClassHolderSource {
for (FieldHolder field : original.getFields()) { for (FieldHolder field : original.getFields()) {
copy.addField(copyField(field)); copy.addField(copyField(field));
} }
copy.setOwnerName(original.getOwnerName());
copyAnnotations(original.getAnnotations(), copy.getAnnotations()); copyAnnotations(original.getAnnotations(), copy.getAnnotations());
return copy; return copy;
} }

View File

@ -57,6 +57,9 @@ class ClassRefsRenamer implements InstructionVisitor {
cls.removeField(field); cls.removeField(field);
renamedCls.addField(field); renamedCls.addField(field);
} }
if (cls.getOwnerName() != null) {
renamedCls.setOwnerName(classNameMapper.map(cls.getOwnerName()));
}
rename(cls.getAnnotations(), renamedCls.getAnnotations()); rename(cls.getAnnotations(), renamedCls.getAnnotations());
for (String iface : cls.getInterfaces()) { for (String iface : cls.getInterfaces()) {
renamedCls.getInterfaces().add(classNameMapper.map(iface)); renamedCls.getInterfaces().add(classNameMapper.map(iface));

View File

@ -64,6 +64,14 @@ public class Parser {
MethodNode methodNode = (MethodNode)obj; MethodNode methodNode = (MethodNode)obj;
cls.addMethod(parseMethod(methodNode, node.name)); cls.addMethod(parseMethod(methodNode, node.name));
} }
if (node.outerClass != null) {
cls.setOwnerName(node.outerClass.replace('/', '.'));
} else {
int lastIndex = node.name.lastIndexOf('$');
if (lastIndex != -1) {
cls.setOwnerName(node.name.substring(0, lastIndex).replace('/', '.'));
}
}
parseAnnotations(cls.getAnnotations(), node); parseAnnotations(cls.getAnnotations(), node);
return cls; return cls;
} }