diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java b/classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java index 6887bbe7f..eb317a27d 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java @@ -26,15 +26,20 @@ import org.teavm.model.Instruction; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; import org.teavm.model.Program; +import org.teavm.model.ValueType; import org.teavm.model.Variable; import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.ClassConstantInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.StringConstantInstruction; public class ClassForNameTransformer implements ClassHolderTransformer { private static final MethodReference getNameMethod = new MethodReference(Class.class, "getName", String.class); private static final MethodReference forNameMethod = new MethodReference(Class.class, "forName", String.class, boolean.class, ClassLoader.class, Class.class); + private static final MethodReference forNameShortMethod = new MethodReference(Class.class, "forName", + String.class, Class.class); private static final MethodReference initMethod = new MethodReference(Class.class, "initialize", void.class); @Override @@ -42,17 +47,18 @@ public class ClassForNameTransformer implements ClassHolderTransformer { for (MethodHolder method : cls.getMethods()) { Program program = method.getProgram(); if (program != null) { - transformProgram(program); + transformProgram(program, innerSource); } } } - private void transformProgram(Program program) { + private void transformProgram(Program program, ClassReaderSource classSource) { DisjointSet varSet = new DisjointSet(); for (int i = 0; i < program.variableCount(); i++) { varSet.create(); } int[] nameIndexes = new int[program.variableCount()]; + String[] constants = new String[program.variableCount()]; Arrays.fill(nameIndexes, -1); for (BasicBlock block : program.getBasicBlocks()) { @@ -64,6 +70,9 @@ public class ClassForNameTransformer implements ClassHolderTransformer { nameIndexes[invoke.getReceiver().getIndex()] = invoke.getInstance().getIndex(); } } + } else if (instruction instanceof StringConstantInstruction) { + StringConstantInstruction stringConstant = (StringConstantInstruction) instruction; + constants[stringConstant.getReceiver().getIndex()] = stringConstant.getConstant(); } else if (instruction instanceof AssignInstruction) { AssignInstruction assign = (AssignInstruction) instruction; varSet.union(assign.getAssignee().getIndex(), assign.getReceiver().getIndex()); @@ -74,6 +83,7 @@ public class ClassForNameTransformer implements ClassHolderTransformer { nameIndexes = Arrays.copyOf(nameIndexes, varSet.size()); int[] nameRepresentatives = new int[nameIndexes.length]; Arrays.fill(nameRepresentatives, -1); + String[] constantsByClasses = new String[varSet.size()]; for (int i = 0; i < program.variableCount(); i++) { int varClass = varSet.find(i); @@ -83,6 +93,8 @@ public class ClassForNameTransformer implements ClassHolderTransformer { if (nameIndexes[i] >= 0) { nameIndexes[varClass] = varSet.find(nameIndexes[i]); } + + constantsByClasses[varClass] = constants[i]; } for (BasicBlock block : program.getBasicBlocks()) { @@ -92,18 +104,31 @@ public class ClassForNameTransformer implements ClassHolderTransformer { } InvokeInstruction invoke = (InvokeInstruction) instruction; - if (!invoke.getMethod().equals(forNameMethod)) { + if (!invoke.getMethod().equals(forNameMethod) && !invoke.getMethod().equals(forNameShortMethod)) { continue; } + Variable representative; + int classNameIndex = invoke.getArguments().get(0).getIndex(); int nameIndex = nameIndexes[classNameIndex]; - if (nameIndex < 0) { + String constant = constantsByClasses[invoke.getArguments().get(0).getIndex()]; + if (nameIndex >= 0) { + representative = program.variableAt(nameRepresentatives[nameIndex]); + } else if (constant != null) { + if (classSource.get(constant) == null) { + continue; + } + ClassConstantInstruction classConstant = new ClassConstantInstruction(); + classConstant.setConstant(ValueType.object(constant)); + classConstant.setReceiver(program.createVariable()); + classConstant.setLocation(invoke.getLocation()); + invoke.insertPrevious(classConstant); + representative = classConstant.getReceiver(); + } else { continue; } - Variable representative = program.variableAt(nameRepresentatives[nameIndex]); - InvokeInstruction initInvoke = new InvokeInstruction(); initInvoke.setLocation(invoke.getLocation()); initInvoke.setType(InvocationType.SPECIAL); diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java b/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java index 80af233e0..8a253c4aa 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ReflectionDependencyListener.java @@ -54,6 +54,9 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { Constructor[].class); private MethodReference getMethods = new MethodReference(Class.class, "getDeclaredMethods", Method[].class); + private MethodReference forName = new MethodReference(Class.class, "forName", String.class, Boolean.class, + ClassLoader.class, Class.class); + private MethodReference forNameShort = new MethodReference(Class.class, "forName", String.class, Class.class); private boolean fieldGetHandled; private boolean fieldSetHandled; private boolean newInstanceHandled; @@ -62,11 +65,17 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { private Map> accessibleMethodCache = new LinkedHashMap<>(); private Set classesWithReflectableFields = new LinkedHashSet<>(); private Set classesWithReflectableMethods = new LinkedHashSet<>(); + private DependencyNode allClasses; public ReflectionDependencyListener(List reflectionSuppliers) { this.reflectionSuppliers = reflectionSuppliers; } + @Override + public void started(DependencyAgent agent) { + allClasses = agent.createNode(); + } + public Set getClassesWithReflectableFields() { return classesWithReflectableFields; } @@ -83,6 +92,11 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { return accessibleMethodCache.get(className); } + @Override + public void classReached(DependencyAgent agent, String className, CallLocation location) { + allClasses.propagate(agent.getType(className)); + } + @Override public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { if (method.getReference().equals(fieldGet)) { @@ -97,14 +111,29 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { method.getVariable(0).getClassValueNode().addConsumer(type -> { if (!type.getName().startsWith("[")) { classesWithReflectableFields.add(type.getName()); + + ClassReader cls = agent.getClassSource().get(type.getName()); + for (FieldReader field : cls.getFields()) { + linkType(agent, field.getType()); + } } }); } else if (method.getReference().equals(getConstructors) || method.getReference().equals(getMethods)) { method.getVariable(0).getClassValueNode().addConsumer(type -> { if (!type.getName().startsWith("[")) { classesWithReflectableMethods.add(type.getName()); + + ClassReader cls = agent.getClassSource().get(type.getName()); + for (MethodReader reflectableMethod : cls.getMethods()) { + linkType(agent, reflectableMethod.getResultType()); + for (ValueType param : reflectableMethod.getParameterTypes()) { + linkType(agent, param); + } + } } }); + } else if (method.getReference().equals(forName) || method.getReference().equals(forNameShort)) { + allClasses.connect(method.getResult().getClassValueNode()); } } @@ -213,6 +242,14 @@ public class ReflectionDependencyListener extends AbstractDependencyListener { }); } + private void linkType(DependencyAgent agent, ValueType type) { + while (type instanceof ValueType.Array) { + type = ((ValueType.Array) type).getItemType(); + } + if (type instanceof ValueType.Object) { + agent.linkClass(((ValueType.Object) type).getClassName(), null); + } + } private void linkClassIfNecessary(DependencyAgent agent, MemberReader member, CallLocation location) { if (member.hasModifier(ElementModifier.STATIC)) { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java index 8db418c6a..3e08148d3 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java @@ -159,7 +159,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin { ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class); Set accessibleMethods = reflection.getAccessibleMethods(className); - ClassReader cls = context.getClassSource().get(className); + ClassReader cls = context.getInitialClassSource().get(className); if (cls == null) { return; } diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 9753f7ca7..9a1abcd12 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -250,7 +250,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { if (debugEmitterToUse == null) { debugEmitterToUse = new DummyDebugInformationEmitter(); } - RenderingContext renderingContext = new RenderingContext(debugEmitterToUse, classes, + RenderingContext renderingContext = new RenderingContext(debugEmitterToUse, + controller.getUnprocessedClassSource(), classes, controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming); renderingContext.setMinifying(minifying); Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index fc9287e5f..30b0abeb6 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -41,6 +41,7 @@ import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.DummyDebugInformationEmitter; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; import org.teavm.model.ListableClassReaderSource; @@ -859,6 +860,11 @@ public class Renderer implements RenderingManager { return classSource; } + @Override + public ClassReaderSource getInitialClassSource() { + return context.getInitialClassSource(); + } + @Override public ClassLoader getClassLoader() { return classLoader; diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java index 5fbd47cb2..d883a9774 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java @@ -33,6 +33,7 @@ import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.interop.PlatformMarker; import org.teavm.model.AnnotationReader; import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; @@ -41,6 +42,7 @@ import org.teavm.model.ValueType; public class RenderingContext { private final DebugInformationEmitter debugEmitter; + private ClassReaderSource initialClassSource; private ListableClassReaderSource classSource; private ClassLoader classLoader; private ServiceRepository services; @@ -53,10 +55,12 @@ public class RenderingContext { private final Map injectorMap = new HashMap<>(); private boolean minifying; - public RenderingContext(DebugInformationEmitter debugEmitter, ListableClassReaderSource classSource, + public RenderingContext(DebugInformationEmitter debugEmitter, + ClassReaderSource initialClassSource, ListableClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, Properties properties, NamingStrategy naming) { this.debugEmitter = debugEmitter; + this.initialClassSource = initialClassSource; this.classSource = classSource; this.classLoader = classLoader; this.services = services; @@ -64,6 +68,10 @@ public class RenderingContext { this.naming = naming; } + public ClassReaderSource getInitialClassSource() { + return initialClassSource; + } + public ListableClassReaderSource getClassSource() { return classSource; } diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java index 015a57d11..826a651ed 100644 --- a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java @@ -18,6 +18,7 @@ package org.teavm.backend.javascript.spi; import java.util.Properties; import org.teavm.common.ServiceRepository; import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.ClassReaderSource; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -25,6 +26,8 @@ import org.teavm.model.ValueType; public interface GeneratorContext extends ServiceRepository { String getParameterName(int index); + ClassReaderSource getInitialClassSource(); + ListableClassReaderSource getClassSource(); ClassLoader getClassLoader(); diff --git a/platform/src/main/java/org/teavm/platform/plugin/ClassLookupDependencySupport.java b/platform/src/main/java/org/teavm/platform/plugin/ClassLookupDependencySupport.java index c5c4320e8..cc2a1642b 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ClassLookupDependencySupport.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ClassLookupDependencySupport.java @@ -41,6 +41,7 @@ public class ClassLookupDependencySupport extends AbstractDependencyListener { if (cls == null) { return; } + MethodReader initMethod = cls.getMethod(new MethodDescriptor("", void.class)); if (initMethod != null) { agent.linkMethod(initMethod.getReference(), location).use();