From 653caa00b3cd21719090cd6ff0b0778f0af988db Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 18 Apr 2017 00:08:09 +0300 Subject: [PATCH] Fixes for html4j/ko4j 1. Replace Class.forName(cls.getName) with class initializer 2. Reduce set of compiled annotations to classes that actually get called getAnnotations() --- .../impl/ClassForNameTransformer.java | 126 ++++++++++++++++++ .../org/teavm/classlib/impl/JCLPlugin.java | 3 +- .../java/lang/ClassDependencyListener.java | 34 +++++ .../org/teavm/classlib/java/lang/TClass.java | 6 + .../reflect/AnnotationDependencyListener.java | 38 ++++-- .../java/org/teavm/platform/Platform.java | 2 +- .../platform/plugin/PlatformGenerator.java | 8 +- 7 files changed, 197 insertions(+), 20 deletions(-) create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/lang/ClassDependencyListener.java diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java b/classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java new file mode 100644 index 000000000..6887bbe7f --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/ClassForNameTransformer.java @@ -0,0 +1,126 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.impl; + +import java.util.Arrays; +import org.teavm.common.DisjointSet; +import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.BasicBlock; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.Instruction; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.Variable; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; + +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 initMethod = new MethodReference(Class.class, "initialize", void.class); + + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { + for (MethodHolder method : cls.getMethods()) { + Program program = method.getProgram(); + if (program != null) { + transformProgram(program); + } + } + } + + private void transformProgram(Program program) { + DisjointSet varSet = new DisjointSet(); + for (int i = 0; i < program.variableCount(); i++) { + varSet.create(); + } + int[] nameIndexes = new int[program.variableCount()]; + Arrays.fill(nameIndexes, -1); + + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + if (instruction instanceof InvokeInstruction) { + InvokeInstruction invoke = (InvokeInstruction) instruction; + if (invoke.getMethod().equals(getNameMethod)) { + if (invoke.getReceiver() != null) { + nameIndexes[invoke.getReceiver().getIndex()] = invoke.getInstance().getIndex(); + } + } + } else if (instruction instanceof AssignInstruction) { + AssignInstruction assign = (AssignInstruction) instruction; + varSet.union(assign.getAssignee().getIndex(), assign.getReceiver().getIndex()); + } + } + } + + nameIndexes = Arrays.copyOf(nameIndexes, varSet.size()); + int[] nameRepresentatives = new int[nameIndexes.length]; + Arrays.fill(nameRepresentatives, -1); + + for (int i = 0; i < program.variableCount(); i++) { + int varClass = varSet.find(i); + if (nameRepresentatives[varClass] < 0) { + nameRepresentatives[varClass] = i; + } + if (nameIndexes[i] >= 0) { + nameIndexes[varClass] = varSet.find(nameIndexes[i]); + } + } + + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + if (!(instruction instanceof InvokeInstruction)) { + continue; + } + InvokeInstruction invoke = (InvokeInstruction) instruction; + + if (!invoke.getMethod().equals(forNameMethod)) { + continue; + } + + int classNameIndex = invoke.getArguments().get(0).getIndex(); + int nameIndex = nameIndexes[classNameIndex]; + if (nameIndex < 0) { + continue; + } + + Variable representative = program.variableAt(nameRepresentatives[nameIndex]); + + InvokeInstruction initInvoke = new InvokeInstruction(); + initInvoke.setLocation(invoke.getLocation()); + initInvoke.setType(InvocationType.SPECIAL); + initInvoke.setMethod(initMethod); + initInvoke.setInstance(representative); + invoke.insertPrevious(initInvoke); + + if (invoke.getReceiver() == null) { + invoke.delete(); + } else { + AssignInstruction assign = new AssignInstruction(); + assign.setLocation(invoke.getLocation()); + assign.setAssignee(representative); + assign.setReceiver(invoke.getReceiver()); + invoke.replace(assign); + } + } + } + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index 1070ae8cd..9aa8695dc 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -21,10 +21,10 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ServiceLoader; +import org.teavm.backend.javascript.TeaVMJavaScriptHost; import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor; import org.teavm.classlib.impl.unicode.CLDRReader; import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener; -import org.teavm.backend.javascript.TeaVMJavaScriptHost; import org.teavm.model.MethodReference; import org.teavm.platform.PlatformClass; import org.teavm.vm.spi.TeaVMHost; @@ -47,6 +47,7 @@ public class JCLPlugin implements TeaVMPlugin { host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader())); + host.add(new ClassForNameTransformer()); host.add(new AnnotationDependencyListener()); host.add(new MethodReference(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class, diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassDependencyListener.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassDependencyListener.java new file mode 100644 index 000000000..c49d83659 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassDependencyListener.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.lang; + +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyPlugin; +import org.teavm.dependency.MethodDependency; +import org.teavm.model.CallLocation; + +public class ClassDependencyListener implements DependencyPlugin { + @Override + public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { + switch (method.getMethod().getName()) { + case "initialize": + method.getVariable(0).getClassValueNode().addConsumer(type -> agent + .linkClass(type.getName(), location) + .initClass(location)); + break; + } + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index 8243bcb6c..e998e9bc6 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -21,6 +21,7 @@ import java.util.Map; import org.teavm.classlib.impl.DeclaringClassMetadataGenerator; import org.teavm.classlib.java.lang.annotation.TAnnotation; import org.teavm.classlib.java.lang.reflect.TAnnotatedElement; +import org.teavm.dependency.PluggableDependency; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; import org.teavm.platform.Platform; @@ -159,6 +160,11 @@ public class TClass extends TObject implements TAnnotatedElement { return forName(name); } + @PluggableDependency(ClassDependencyListener.class) + void initialize() { + Platform.initClass(platformClass); + } + @SuppressWarnings({ "unchecked", "unused" }) public T newInstance() throws TInstantiationException, TIllegalAccessException { Object instance = Platform.newInstance(platformClass); diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java index 41b56e434..c102c2f8f 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import org.teavm.dependency.AbstractDependencyListener; import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyNode; import org.teavm.dependency.MethodDependency; import org.teavm.model.AccessLevel; import org.teavm.model.AnnotationReader; @@ -33,26 +34,13 @@ import org.teavm.model.ElementModifier; import org.teavm.model.FieldHolder; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.model.emit.ProgramEmitter; import org.teavm.model.emit.ValueEmitter; import org.teavm.platform.PlatformAnnotationProvider; public class AnnotationDependencyListener extends AbstractDependencyListener { - @Override - public void classReached(DependencyAgent agent, String className, CallLocation location) { - ClassReader cls = agent.getClassSource().get(className); - if (cls == null) { - return; - } - - for (AnnotationReader annotation : cls.getAnnotations().all()) { - agent.linkClass(annotation.getType(), location); - } - - createAnnotationClass(agent, className); - } - private String getAnnotationImplementor(DependencyAgent agent, String annotationType) { String implementorName = annotationType + "$$_impl"; if (agent.getClassSource().get(implementorName) == null) { @@ -145,6 +133,28 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { } } } + + MethodReference methodRef = method.getMethod().getReference(); + if (methodRef.getClassName().equals("java.lang.Class") && methodRef.getName().equals("getAnnotations")) { + reachGetAnnotations(agent, location, method.getVariable(0)); + } + } + + private void reachGetAnnotations(DependencyAgent agent, CallLocation location, DependencyNode node) { + node.getClassValueNode().addConsumer(type -> { + String className = type.getName(); + + ClassReader cls = agent.getClassSource().get(className); + if (cls == null) { + return; + } + + for (AnnotationReader annotation : cls.getAnnotations().all()) { + agent.linkClass(annotation.getType(), location); + } + + createAnnotationClass(agent, className); + }); } private void createAnnotationClass(DependencyAgent agent, String className) { diff --git a/platform/src/main/java/org/teavm/platform/Platform.java b/platform/src/main/java/org/teavm/platform/Platform.java index 177bb50dc..4e9eca8e9 100644 --- a/platform/src/main/java/org/teavm/platform/Platform.java +++ b/platform/src/main/java/org/teavm/platform/Platform.java @@ -108,8 +108,8 @@ public final class Platform { @PluggableDependency(PlatformGenerator.class) public static native PlatformClass lookupClass(String name); - @GeneratedBy(PlatformGenerator.class) @PluggableDependency(PlatformGenerator.class) + @InjectedBy(PlatformGenerator.class) public static native void initClass(PlatformClass cls); @InjectedBy(PlatformGenerator.class) diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java index 4537f2ea7..a90a2e222 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java @@ -35,10 +35,6 @@ import org.teavm.platform.Platform; import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformRunnable; -/** - * - * @author Alexey Andreev - */ public class PlatformGenerator implements Generator, Injector, DependencyPlugin { @Override public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { @@ -73,6 +69,10 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin case "getPlatformObject": context.writeExpr(context.getArgument(0)); break; + case "initClass": + context.writeExpr(context.getArgument(0)); + context.getWriter().append(".$clinit()"); + break; } }