diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java index 62260ae6a..149f5822d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java @@ -49,10 +49,10 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency graph) { - switch (graph.getReference().getName()) { + public void methodAchieved(DependencyChecker checker, MethodDependency method) { + switch (method.getReference().getName()) { case "obtainDigitMapping": - achieveObtainDigitMapping(graph); + achieveObtainDigitMapping(method); break; } } @@ -72,7 +72,7 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin { .append("\");").softNewLine(); } - private void achieveObtainDigitMapping(MethodDependency graph) { - graph.getResult().propagate("java.lang.String"); + private void achieveObtainDigitMapping(MethodDependency method) { + method.getResult().propagate("java.lang.String"); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java index ee05eee51..a7ca2e66b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java @@ -62,16 +62,16 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency graph) { - switch (graph.getReference().getName()) { + public void methodAchieved(DependencyChecker checker, MethodDependency method) { + switch (method.getReference().getName()) { case "clone": - achieveClone(graph); + achieveClone(method); break; case "getClass": - achieveGetClass(checker, graph); + achieveGetClass(checker, method); break; case "wrap": - achieveWrap(graph); + achieveWrap(method); break; } } @@ -87,12 +87,12 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu writer.append(".constructor)"); } - private void achieveGetClass(DependencyChecker checker, MethodDependency graph) { + private void achieveGetClass(DependencyChecker checker, MethodDependency method) { String classClass = "java.lang.Class"; MethodReference initMethod = new MethodReference(classClass, new MethodDescriptor("createNew", ValueType.object(classClass))); checker.addEntryPoint(initMethod); - graph.getResult().propagate("java.lang.Class"); + method.getResult().propagate("java.lang.Class"); } private void generateHashCode(GeneratorContext context, SourceWriter writer) throws IOException { @@ -108,8 +108,8 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu writer.append("return copy;").softNewLine(); } - private void achieveClone(MethodDependency graph) { - graph.getVariable(0).connect(graph.getResult()); + private void achieveClone(MethodDependency method) { + method.getVariable(0).connect(method.getResult()); } private void generateWrap(InjectorContext context) throws IOException { diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java index 876b66329..b24ac2f38 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java @@ -43,10 +43,10 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency graph) { - switch (graph.getReference().getName()) { + public void methodAchieved(DependencyChecker checker, MethodDependency method) { + switch (method.getReference().getName()) { case "doArrayCopy": - achieveArrayCopy(graph); + achieveArrayCopy(method); break; } } @@ -66,9 +66,9 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin { writer.append("return Long_fromNumber(new Date().getTime());").softNewLine(); } - private void achieveArrayCopy(MethodDependency graph) { - DependencyNode src = graph.getVariable(1); - DependencyNode dest = graph.getVariable(3); + private void achieveArrayCopy(MethodDependency method) { + DependencyNode src = method.getVariable(1); + DependencyNode dest = method.getVariable(3); src.getArrayItem().connect(dest.getArrayItem()); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index 8dc752ed3..ee15df29c 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -33,9 +33,9 @@ import org.teavm.model.ValueType; */ public class ArrayNativeGenerator implements Generator, DependencyPlugin { @Override - public void methodAchieved(DependencyChecker checker, MethodDependency graph) { - if (graph.getReference().getName().equals("getLength")) { - achieveGetLength(checker, graph); + public void methodAchieved(DependencyChecker checker, MethodDependency method) { + if (method.getReference().getName().equals("getLength")) { + achieveGetLength(checker, method); } } @@ -59,8 +59,8 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { writer.append("return " + array + ".data.length;").softNewLine(); } - private void achieveGetLength(final DependencyChecker checker, final MethodDependency graph) { - graph.getVariable(1).addConsumer(new DependencyConsumer() { + private void achieveGetLength(final DependencyChecker checker, final MethodDependency method) { + method.getVariable(1).addConsumer(new DependencyConsumer() { @Override public void consume(String type) { if (!type.startsWith("[")) { MethodReference cons = new MethodReference("java.lang.IllegalArgumentException", diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index de37a8918..873e45b77 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -26,7 +26,7 @@ import org.teavm.model.*; * * @author Alexey Andreev */ -public class DependencyChecker implements DependencyInformation { +public class DependencyChecker implements DependencyInfo { private static Object dummyValue = new Object(); static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true"); private ClassReaderSource classSource; @@ -34,12 +34,11 @@ public class DependencyChecker implements DependencyInformation { private FiniteExecutor executor; private Mapper methodReaderCache; private Mapper fieldReaderCache; - private ConcurrentMap abstractMethods = new ConcurrentHashMap<>(); private ConcurrentMap stacks = new ConcurrentHashMap<>(); private ConcurrentMap fieldStacks = new ConcurrentHashMap<>(); private ConcurrentMap classStacks = new ConcurrentHashMap<>(); private ConcurrentCachedMapper methodCache; - private ConcurrentCachedMapper fieldCache; + private ConcurrentCachedMapper fieldCache; private ConcurrentMap achievableClasses = new ConcurrentHashMap<>(); private ConcurrentMap initializedClasses = new ConcurrentHashMap<>(); private List listeners = new ArrayList<>(); @@ -75,8 +74,8 @@ public class DependencyChecker implements DependencyInformation { return createMethodDep(preimage, method, stacks.get(preimage)); } }); - fieldCache = new ConcurrentCachedMapper<>(new Mapper() { - @Override public DependencyNode map(FieldReference preimage) { + fieldCache = new ConcurrentCachedMapper<>(new Mapper() { + @Override public FieldDependency map(FieldReference preimage) { FieldReader field = fieldReaderCache.map(preimage); if (field != null && !field.getReference().equals(preimage)) { fieldStacks.put(field.getReference(), fieldStacks.get(preimage)); @@ -98,10 +97,10 @@ public class DependencyChecker implements DependencyInformation { }); fieldCache.addKeyListener(new KeyListener() { @Override public void keyAdded(FieldReference key) { - DependencyNode node = fieldCache.getKnown(key); - if (!missingFields.containsKey(key)) { + FieldDependency fieldDep = fieldCache.getKnown(key); + if (!fieldDep.isMissing()) { for (DependencyListener listener : listeners) { - listener.fieldAchieved(DependencyChecker.this, key, node); + listener.fieldAchieved(DependencyChecker.this, fieldDep); } } } @@ -131,8 +130,9 @@ public class DependencyChecker implements DependencyInformation { if (parameters.length != argumentTypes.length) { throw new IllegalArgumentException("argumentTypes length does not match the number of method's arguments"); } - MethodDependency graph = linkMethod(methodRef, DependencyStack.ROOT); - DependencyNode[] varNodes = graph.getVariables(); + MethodDependency method = linkMethod(methodRef, DependencyStack.ROOT); + method.use(); + DependencyNode[] varNodes = method.getVariables(); varNodes[0].propagate(methodRef.getClassName()); for (int i = 0; i < argumentTypes.length; ++i) { varNodes[i + 1].propagate(argumentTypes[i]); @@ -183,7 +183,7 @@ public class DependencyChecker implements DependencyInformation { } if (cls.getMethod(clinitDesc) != null) { MethodReference methodRef = new MethodReference(className, clinitDesc); - linkMethod(methodRef, new DependencyStack(methodRef, stack)); + linkMethod(methodRef, new DependencyStack(methodRef, stack)).use(); } className = cls.getParent(); } @@ -264,11 +264,10 @@ public class DependencyChecker implements DependencyInformation { final MethodDependency dep = new MethodDependency(parameterNodes, paramCount, resultNode, stack, method, methodRef); if (method != null) { - final MethodReader currentMethod = method; executor.execute(new Runnable() { @Override public void run() { DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyChecker.this); - graphBuilder.buildGraph(currentMethod, dep); + graphBuilder.buildGraph(dep); } }); } else { @@ -284,10 +283,6 @@ public class DependencyChecker implements DependencyInformation { return methodCache.caches(methodRef); } - public boolean isAbstractMethodAchievable(MethodReference methodRef) { - return abstractMethods.containsKey(methodRef); - } - @Override public Collection getAchievableMethods() { return methodCache.getCachedPreimages(); @@ -303,17 +298,17 @@ public class DependencyChecker implements DependencyInformation { return new HashSet<>(achievableClasses.keySet()); } - public DependencyNode linkField(FieldReference fieldRef, DependencyStack stack) { + public FieldDependency linkField(FieldReference fieldRef, DependencyStack stack) { fieldStacks.putIfAbsent(fieldRef, stack); return fieldCache.map(fieldRef); } @Override - public DependencyNode getField(FieldReference fieldRef) { + public FieldDependency getField(FieldReference fieldRef) { return fieldCache.getKnown(fieldRef); } - private DependencyNode createFieldNode(FieldReference fieldRef, FieldReader field, DependencyStack stack) { + private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field, DependencyStack stack) { DependencyNode node = new DependencyNode(this); if (field == null) { missingFields.putIfAbsent(fieldRef, stack); @@ -324,24 +319,15 @@ public class DependencyChecker implements DependencyInformation { if (field != null) { initClass(fieldRef.getClassName(), stack); } - return node; + return new FieldDependency(node, stack, field, fieldRef); } - private void activateDependencyPlugin(MethodDependency graph) { - MethodReference methodRef = graph.getReference(); - ClassReader cls = classSource.get(methodRef.getClassName()); - if (cls == null) { - return; - } - MethodReader method = cls.getMethod(methodRef.getDescriptor()); - if (method == null) { - return; - } - AnnotationHolder depAnnot = method.getAnnotations().get(PluggableDependency.class.getName()); + private void activateDependencyPlugin(MethodDependency methodDep) { + AnnotationReader depAnnot = methodDep.getMethod().getAnnotations().get(PluggableDependency.class.getName()); if (depAnnot == null) { return; } - ValueType depType = depAnnot.getValues().get("value").getJavaClass(); + ValueType depType = depAnnot.getValue("value").getJavaClass(); String depClassName = ((ValueType.Object)depType).getClassName(); Class depClass; try { @@ -355,26 +341,7 @@ public class DependencyChecker implements DependencyInformation { } catch (IllegalAccessException | InstantiationException e) { throw new RuntimeException("Can't instantiate dependency plugin " + depClassName, e); } - plugin.methodAchieved(this, graph); - } - - public void addAbstractMethod(MethodReference methodRef, DependencyStack stack) { - stacks.putIfAbsent(methodRef, stack); - if (abstractMethods.putIfAbsent(methodRef, methodRef) == null) { - String className = methodRef.getClassName(); - while (className != null) { - ClassReader cls = classSource.get(className); - if (cls == null) { - return; - } - MethodReader method = cls.getMethod(methodRef.getDescriptor()); - if (method != null) { - abstractMethods.put(methodRef, methodRef); - return; - } - className = cls.getParent(); - } - } + plugin.methodAchieved(this, methodDep); } public ListableClassHolderSource cutUnachievableClasses(ClassHolderSource classSource) { @@ -384,13 +351,12 @@ public class DependencyChecker implements DependencyInformation { cutClasses.putClassHolder(classHolder); for (MethodHolder method : classHolder.getMethods().toArray(new MethodHolder[0])) { MethodReference methodRef = new MethodReference(className, method.getDescriptor()); - if (!methodCache.getCachedPreimages().contains(methodRef)) { - if (abstractMethods.containsKey(methodRef)) { - method.getModifiers().add(ElementModifier.ABSTRACT); - method.setProgram(null); - } else { - classHolder.removeMethod(method); - } + MethodDependency methodDep = getMethod(methodRef); + if (methodDep == null) { + classHolder.removeMethod(method); + } else if (!methodDep.isUsed()) { + method.getModifiers().add(ElementModifier.ABSTRACT); + method.setProgram(null); } } for (FieldHolder field : classHolder.getFields().toArray(new FieldHolder[0])) { @@ -404,7 +370,7 @@ public class DependencyChecker implements DependencyInformation { } @Override - public DependencyMethodInformation getMethod(MethodReference methodRef) { + public MethodDependency getMethod(MethodReference methodRef) { return methodCache.getKnown(methodRef); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 78a5fa801..47229da51 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -15,6 +15,7 @@ */ package org.teavm.dependency; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -32,23 +33,25 @@ class DependencyGraphBuilder { private DependencyNode resultNode; private ProgramReader program; private DependencyStack callerStack; + private List useRunners = new ArrayList<>(); public DependencyGraphBuilder(DependencyChecker dependencyChecker) { this.dependencyChecker = dependencyChecker; } - public void buildGraph(MethodReader method, MethodDependency graph) { + public void buildGraph(MethodDependency dep) { + MethodReader method = dep.getMethod(); if (method.getProgram() == null || method.getProgram().basicBlockCount() == 0) { return; } - callerStack = graph.getStack(); + callerStack = dep.getStack(); program = method.getProgram(); if (DependencyChecker.shouldLog) { System.out.println("Method achieved: " + method.getReference()); System.out.println(new ListingBuilder().buildListing(program, " ")); } - resultNode = graph.getResult(); - nodes = graph.getVariables(); + resultNode = dep.getResult(); + nodes = dep.getVariables(); for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlockReader block = program.basicBlockAt(i); block.readAllInstructions(reader); @@ -58,6 +61,7 @@ class DependencyGraphBuilder { } } } + dep.setUseRunner(new MultipleRunner(useRunners)); } private static class VirtualCallPropagationListener implements DependencyConsumer { @@ -92,6 +96,7 @@ class DependencyGraphBuilder { MethodReference methodRef = new MethodReference(className, methodDesc); MethodDependency methodDep = checker.linkMethod(methodRef, stack); if (!methodDep.isMissing() && knownMethods.putIfAbsent(methodRef, methodRef) == null) { + methodDep.use(); DependencyNode[] targetParams = methodDep.getVariables(); for (int i = 0; i < parameters.length; ++i) { parameters[i].connect(targetParams[i]); @@ -103,6 +108,30 @@ class DependencyGraphBuilder { } } + private static class MultipleRunner implements Runnable { + private List parts; + public MultipleRunner(List parts) { + this.parts = parts; + } + @Override public void run() { + for (Runnable part : parts) { + part.run(); + } + } + } + + private static class TypePropagationRunner implements Runnable { + private DependencyNode node; + private String type; + public TypePropagationRunner(DependencyNode node, String type) { + this.node = node; + this.type = type; + } + @Override public void run() { + node.propagate(type); + } + } + private InstructionReader reader = new InstructionReader() { @Override public void nop() { @@ -110,7 +139,7 @@ class DependencyGraphBuilder { @Override public void classConstant(VariableReader receiver, ValueType cst) { - nodes[receiver.getIndex()].propagate("java.lang.Class"); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], "java.lang.Class")); while (cst instanceof ValueType.Array) { cst = ((ValueType.Array)cst).getItemType(); } @@ -141,9 +170,11 @@ class DependencyGraphBuilder { @Override public void stringConstant(VariableReader receiver, String cst) { - nodes[receiver.getIndex()].propagate("java.lang.String"); - dependencyChecker.linkMethod(new MethodReference("java.lang.String", new MethodDescriptor( - "", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), callerStack); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], "java.lang.String")); + MethodDependency method = dependencyChecker.linkMethod(new MethodReference("java.lang.String", + new MethodDescriptor("", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), + callerStack); + method.use(); } @Override @@ -211,7 +242,7 @@ class DependencyGraphBuilder { @Override public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { - nodes[receiver.getIndex()].propagate("[" + itemType); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], "[" + itemType)); } @Override @@ -222,27 +253,27 @@ class DependencyGraphBuilder { sb.append('['); } sb.append(itemType); - nodes[receiver.getIndex()].propagate(sb.toString()); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], sb.toString())); } @Override public void create(VariableReader receiver, String type) { - nodes[receiver.getIndex()].propagate(type); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], type)); } @Override public void getField(VariableReader receiver, VariableReader instance, FieldReference field, ValueType fieldType) { - DependencyNode fieldNode = dependencyChecker.linkField(field, callerStack); + FieldDependency fieldDep = dependencyChecker.linkField(field, callerStack); DependencyNode receiverNode = nodes[receiver.getIndex()]; - fieldNode.connect(receiverNode); + fieldDep.getValue().connect(receiverNode); } @Override public void putField(VariableReader instance, FieldReference field, VariableReader value) { - DependencyNode fieldNode = dependencyChecker.linkField(field, callerStack); + FieldDependency fieldDep = dependencyChecker.linkField(field, callerStack); DependencyNode valueNode = nodes[value.getIndex()]; - valueNode.connect(fieldNode); + valueNode.connect(fieldDep.getValue()); } @Override @@ -305,6 +336,7 @@ class DependencyGraphBuilder { if (methodDep.isMissing()) { return; } + methodDep.use(); DependencyNode[] targetParams = methodDep.getVariables(); for (int i = 0; i < arguments.size(); ++i) { nodes[arguments.get(i).getIndex()].connect(targetParams[i + 1]); diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyInformation.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyInfo.java similarity index 84% rename from teavm-core/src/main/java/org/teavm/dependency/DependencyInformation.java rename to teavm-core/src/main/java/org/teavm/dependency/DependencyInfo.java index e5676a80a..f941bf4ad 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyInformation.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyInfo.java @@ -23,14 +23,14 @@ import org.teavm.model.MethodReference; * * @author Alexey Andreev */ -public interface DependencyInformation { +public interface DependencyInfo { Collection getAchievableMethods(); Collection getAchievableFields(); Collection getAchievableClasses(); - DependencyValueInformation getField(FieldReference fieldRef); + FieldDependencyInfo getField(FieldReference fieldRef); - DependencyMethodInformation getMethod(MethodReference methodRef); + MethodDependencyInfo getMethod(MethodReference methodRef); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java index 07b8d5728..1ad57dee1 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java @@ -15,8 +15,6 @@ */ package org.teavm.dependency; -import org.teavm.model.FieldReference; - /** * * @author Alexey Andreev @@ -28,5 +26,5 @@ public interface DependencyListener { void methodAchieved(DependencyChecker dependencyChecker, MethodDependency method); - void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field, DependencyNode node); + void fieldAchieved(DependencyChecker dependencyChecker, FieldDependency field); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java index a7ac48779..452741005 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -24,7 +24,7 @@ import java.util.concurrent.atomic.AtomicReference; * * @author Alexey Andreev */ -public class DependencyNode implements DependencyValueInformation { +public class DependencyNode implements ValueDependencyInfo { private DependencyChecker dependencyChecker; private static final Object mapValue = new Object(); private ConcurrentMap followers = new ConcurrentHashMap<>(); diff --git a/teavm-core/src/main/java/org/teavm/dependency/FieldDependency.java b/teavm-core/src/main/java/org/teavm/dependency/FieldDependency.java new file mode 100644 index 000000000..06b8e185d --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/FieldDependency.java @@ -0,0 +1,58 @@ +/* + * Copyright 2014 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.dependency; + +import org.teavm.model.FieldReader; +import org.teavm.model.FieldReference; + +/** + * + * @author Alexey Andreev + */ +public class FieldDependency implements FieldDependencyInfo { + private DependencyNode value; + private DependencyStack stack; + private FieldReader field; + private FieldReference reference; + + FieldDependency(DependencyNode value, DependencyStack stack, FieldReader field, FieldReference reference) { + this.value = value; + this.stack = stack; + this.field = field; + this.reference = reference; + } + + @Override + public DependencyNode getValue() { + return value; + } + + public DependencyStack getStack() { + return stack; + } + + public FieldReader getField() { + return field; + } + + public FieldReference getReference() { + return reference; + } + + public boolean isMissing() { + return field == null; + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/FieldDependencyInfo.java b/teavm-core/src/main/java/org/teavm/dependency/FieldDependencyInfo.java new file mode 100644 index 000000000..965f5cadc --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/FieldDependencyInfo.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 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.dependency; + +/** + * + * @author Alexey Andreev + */ +public interface FieldDependencyInfo { + ValueDependencyInfo getValue(); +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java index 443e7ca7e..c5f94b01a 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -16,6 +16,7 @@ package org.teavm.dependency; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; @@ -23,13 +24,15 @@ import org.teavm.model.MethodReference; * * @author Alexey Andreev */ -public class MethodDependency implements DependencyMethodInformation { +public class MethodDependency implements MethodDependencyInfo { private DependencyNode[] variableNodes; private int parameterCount; private DependencyNode resultNode; private DependencyStack stack; private MethodReader method; private MethodReference reference; + private AtomicBoolean used = new AtomicBoolean(); + private volatile Runnable useRunner; MethodDependency(DependencyNode[] variableNodes, int parameterCount, DependencyNode resultNode, DependencyStack stack, MethodReader method, MethodReference reference) { @@ -81,4 +84,29 @@ public class MethodDependency implements DependencyMethodInformation { public boolean isMissing() { return method == null; } + + @Override + public boolean isUsed() { + return used.get(); + } + + public void use() { + if (used.compareAndSet(false, true)) { + if (useRunner != null) { + useRunner.run(); + useRunner = null; + } + } + } + + void setUseRunner(Runnable runner) { + if (isUsed()) { + runner.run(); + } else { + useRunner = runner; + if (isUsed()) { + runner.run(); + } + } + } } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyMethodInformation.java b/teavm-core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java similarity index 83% rename from teavm-core/src/main/java/org/teavm/dependency/DependencyMethodInformation.java rename to teavm-core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java index a9a455d2f..2c3f26e46 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyMethodInformation.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java @@ -19,14 +19,16 @@ package org.teavm.dependency; * * @author Alexey Andreev */ -public interface DependencyMethodInformation { - DependencyValueInformation[] getVariables(); +public interface MethodDependencyInfo { + ValueDependencyInfo[] getVariables(); int getVariableCount(); - DependencyValueInformation getVariable(int index); + ValueDependencyInfo getVariable(int index); int getParameterCount(); DependencyNode getResult(); + + boolean isUsed(); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyValueInformation.java b/teavm-core/src/main/java/org/teavm/dependency/ValueDependencyInfo.java similarity index 94% rename from teavm-core/src/main/java/org/teavm/dependency/DependencyValueInformation.java rename to teavm-core/src/main/java/org/teavm/dependency/ValueDependencyInfo.java index b2166aeb5..fb46eb751 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyValueInformation.java +++ b/teavm-core/src/main/java/org/teavm/dependency/ValueDependencyInfo.java @@ -19,7 +19,7 @@ package org.teavm.dependency; * * @author Alexey Andreev */ -public interface DependencyValueInformation { +public interface ValueDependencyInfo { String[] getTypes(); boolean hasType(String type); diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java index 4e79729f7..473ff0164 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java @@ -20,7 +20,7 @@ import java.util.*; import org.teavm.codegen.*; import org.teavm.common.FiniteExecutor; import org.teavm.dependency.DependencyChecker; -import org.teavm.dependency.DependencyInformation; +import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyStack; import org.teavm.javascript.ast.ClassNode; @@ -120,9 +120,9 @@ public class JavascriptBuilder implements JavascriptBuilderHost { builder.setMinified(minifying); SourceWriter sourceWriter = builder.build(writer); dependencyChecker.linkMethod(new MethodReference("java.lang.Class", new MethodDescriptor("createNew", - ValueType.object("java.lang.Class"))), DependencyStack.ROOT); + ValueType.object("java.lang.Class"))), DependencyStack.ROOT).use(); dependencyChecker.linkMethod(new MethodReference("java.lang.String", new MethodDescriptor("", - ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), DependencyStack.ROOT); + ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), DependencyStack.ROOT).use(); executor.complete(); dependencyChecker.checkForMissingItems(); ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(classSource); @@ -164,16 +164,18 @@ public class JavascriptBuilder implements JavascriptBuilderHost { } } - private void devirtualize(ListableClassHolderSource classes, DependencyInformation dependency) { + private void devirtualize(ListableClassHolderSource classes, DependencyInfo dependency) { final Devirtualization devirtualization = new Devirtualization(dependency, classes); for (String className : classes.getClassNames()) { ClassHolder cls = classes.get(className); for (final MethodHolder method : cls.getMethods()) { - executor.execute(new Runnable() { - @Override public void run() { - devirtualization.apply(method); - } - }); + if (method.getProgram() != null) { + executor.execute(new Runnable() { + @Override public void run() { + devirtualization.apply(method); + } + }); + } } } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java index 18b4fcbc8..f4e6f19ce 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java @@ -25,12 +25,13 @@ import org.teavm.model.MethodReference; public class JavascriptEntryPoint { private String publicName; MethodReference reference; - private MethodDependency graph; + private MethodDependency method; - JavascriptEntryPoint(String publicName, MethodReference reference, MethodDependency graph) { + JavascriptEntryPoint(String publicName, MethodReference reference, MethodDependency method) { this.publicName = publicName; this.reference = reference; - this.graph = graph; + this.method = method; + method.use(); } String getPublicName() { @@ -41,7 +42,7 @@ public class JavascriptEntryPoint { if (argument > reference.parameterCount()) { throw new IllegalArgumentException("Illegal argument #" + argument + " of " + reference.parameterCount()); } - graph.getVariable(argument).propagate(type); + method.getVariable(argument).propagate(type); return this; } } diff --git a/teavm-core/src/main/java/org/teavm/model/AnnotationContainerReader.java b/teavm-core/src/main/java/org/teavm/model/AnnotationContainerReader.java index 146dea7b9..a79265728 100644 --- a/teavm-core/src/main/java/org/teavm/model/AnnotationContainerReader.java +++ b/teavm-core/src/main/java/org/teavm/model/AnnotationContainerReader.java @@ -20,7 +20,7 @@ package org.teavm.model; * @author Alexey Andreev */ public interface AnnotationContainerReader { - AnnotationHolder get(String type); + AnnotationReader get(String type); Iterable all(); } diff --git a/teavm-core/src/main/java/org/teavm/optimization/Devirtualization.java b/teavm-core/src/main/java/org/teavm/optimization/Devirtualization.java index 88603164b..69f9182cb 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/Devirtualization.java +++ b/teavm-core/src/main/java/org/teavm/optimization/Devirtualization.java @@ -17,9 +17,9 @@ package org.teavm.optimization; import java.util.HashSet; import java.util.Set; -import org.teavm.dependency.DependencyInformation; -import org.teavm.dependency.DependencyMethodInformation; -import org.teavm.dependency.DependencyValueInformation; +import org.teavm.dependency.DependencyInfo; +import org.teavm.dependency.MethodDependencyInfo; +import org.teavm.dependency.ValueDependencyInfo; import org.teavm.model.*; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; @@ -29,16 +29,16 @@ import org.teavm.model.instructions.InvokeInstruction; * @author Alexey Andreev */ public class Devirtualization { - private DependencyInformation dependency; + private DependencyInfo dependency; private ClassReaderSource classSource; - public Devirtualization(DependencyInformation dependency, ClassReaderSource classSource) { + public Devirtualization(DependencyInfo dependency, ClassReaderSource classSource) { this.dependency = dependency; this.classSource = classSource; } public void apply(MethodHolder method) { - DependencyMethodInformation methodDep = dependency.getMethod(method.getReference()); + MethodDependencyInfo methodDep = dependency.getMethod(method.getReference()); if (methodDep == null) { return; } @@ -53,7 +53,7 @@ public class Devirtualization { if (invoke.getType() != InvocationType.VIRTUAL) { continue; } - DependencyValueInformation var = methodDep.getVariable(invoke.getInstance().getIndex()); + ValueDependencyInfo var = methodDep.getVariable(invoke.getInstance().getIndex()); Set implementations = getImplementations(var.getTypes(), invoke.getMethod().getDescriptor()); if (implementations.size() == 1) { diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java index 40a1752e0..26b69dd92 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -48,44 +48,44 @@ public class JavaScriptBodyDependency implements DependencyListener { } @Override - public void methodAchieved(DependencyChecker dependencyChecker, MethodDependency graph) { - if (graph.isMissing()) { + public void methodAchieved(DependencyChecker dependencyChecker, MethodDependency method) { + if (method.isMissing()) { return; } - AnnotationReader annot = graph.getMethod().getAnnotations().get(JavaScriptBody.class.getName()); + AnnotationReader annot = method.getMethod().getAnnotations().get(JavaScriptBody.class.getName()); if (annot != null) { includeDefaultDependencies(dependencyChecker); AnnotationValue javacall = annot.getValue("javacall"); - if (graph.getResult() != null) { - allClassesNode.connect(graph.getResult()); - allClassesNode.addConsumer(new OneDirectionalConnection(graph.getResult().getArrayItem())); - allClassesNode.addConsumer(new OneDirectionalConnection(graph.getResult().getArrayItem() + if (method.getResult() != null) { + allClassesNode.connect(method.getResult()); + allClassesNode.addConsumer(new OneDirectionalConnection(method.getResult().getArrayItem())); + allClassesNode.addConsumer(new OneDirectionalConnection(method.getResult().getArrayItem() .getArrayItem())); } - for (int i = 0; i < graph.getParameterCount(); ++i) { - graph.getVariable(i).connect(allClassesNode); - allClassesNode.addConsumer(new OneDirectionalConnection(graph.getVariable(i).getArrayItem())); - allClassesNode.addConsumer(new OneDirectionalConnection(graph.getVariable(i).getArrayItem() + for (int i = 0; i < method.getParameterCount(); ++i) { + method.getVariable(i).connect(allClassesNode); + allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem())); + allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem() .getArrayItem())); } if (javacall != null && javacall.getBoolean()) { String body = annot.getValue("body").getString(); - new GeneratorJsCallback(dependencyChecker.getClassSource(), dependencyChecker, graph).parse(body); + new GeneratorJsCallback(dependencyChecker, method).parse(body); } } } private void includeDefaultDependencies(DependencyChecker dependencyChecker) { - dependencyChecker.linkMethod(JavaScriptConvGenerator.fromJsMethod, DependencyStack.ROOT); - dependencyChecker.linkMethod(JavaScriptConvGenerator.toJsMethod, DependencyStack.ROOT); - dependencyChecker.linkMethod(JavaScriptConvGenerator.intValueMethod, DependencyStack.ROOT); - dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, DependencyStack.ROOT); - dependencyChecker.linkMethod(JavaScriptConvGenerator.booleanValueMethod, DependencyStack.ROOT); - dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, DependencyStack.ROOT); + dependencyChecker.linkMethod(JavaScriptConvGenerator.fromJsMethod, DependencyStack.ROOT).use(); + dependencyChecker.linkMethod(JavaScriptConvGenerator.toJsMethod, DependencyStack.ROOT).use(); + dependencyChecker.linkMethod(JavaScriptConvGenerator.intValueMethod, DependencyStack.ROOT).use(); + dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, DependencyStack.ROOT).use(); + dependencyChecker.linkMethod(JavaScriptConvGenerator.booleanValueMethod, DependencyStack.ROOT).use(); + dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, DependencyStack.ROOT).use(); } @Override - public void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field, DependencyNode node) { + public void fieldAchieved(DependencyChecker dependencyChecker, FieldDependency fieldDep) { } private static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) { @@ -113,27 +113,25 @@ public class JavaScriptBodyDependency implements DependencyListener { } private class GeneratorJsCallback extends JsCallback { - private ClassReaderSource classSource; private DependencyChecker dependencyChecker; private MethodDependency caller; - public GeneratorJsCallback(ClassReaderSource classSource, DependencyChecker dependencyChecker, - MethodDependency caller) { - this.classSource = classSource; + public GeneratorJsCallback(DependencyChecker dependencyChecker, MethodDependency caller) { this.dependencyChecker = dependencyChecker; this.caller = caller; } @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) { MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); - MethodReader reader = findMethod(classSource, fqn, desc); - if (reader != null) { + MethodReader reader = findMethod(dependencyChecker.getClassSource(), params, desc); + MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc); + MethodDependency methodDep = dependencyChecker.linkMethod(ref, caller.getStack()); + if (!methodDep.isMissing()) { if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) { - MethodDependency graph = dependencyChecker.linkMethod(reader.getReference(), caller.getStack()); - for (int i = 0; i <= graph.getParameterCount(); ++i) { - allClassesNode.connect(graph.getVariable(i)); + methodDep.use(); + for (int i = 0; i <= methodDep.getParameterCount(); ++i) { + allClassesNode.connect(methodDep.getVariable(i)); } } else { - allClassesNode.addConsumer(new VirtualCallbackConsumer(dependencyChecker, - reader.getReference(), caller)); + allClassesNode.addConsumer(new VirtualCallbackConsumer(dependencyChecker, reader, caller)); } } return ""; @@ -141,22 +139,46 @@ public class JavaScriptBodyDependency implements DependencyListener { } private class VirtualCallbackConsumer implements DependencyConsumer { - private String superClass; private DependencyChecker dependencyChecker; - private MethodReference superMethod; + private MethodReader superMethod; + private ClassReader superClass; private MethodDependency caller; - public VirtualCallbackConsumer(DependencyChecker dependencyChecker, MethodReference superMethod, + public VirtualCallbackConsumer(DependencyChecker dependencyChecker, MethodReader superMethod, MethodDependency caller) { this.dependencyChecker = dependencyChecker; this.superMethod = superMethod; this.caller = caller; + this.superClass = dependencyChecker.getClassSource().get(superMethod.getOwnerName()); } @Override public void consume(String type) { - MethodReference method = new MethodReference(type, superMethod.getDescriptor()); - MethodDependency graph = dependencyChecker.linkMethod(method, caller.getStack()); - for (int i = 0; i < graph.getParameterCount(); ++i) { - allClassesNode.connect(graph.getVariable(i)); + if (!isAssignableFrom(superClass, type)) { + return; } + MethodReference methodRef = new MethodReference(type, superMethod.getDescriptor()); + MethodDependency method = dependencyChecker.linkMethod(methodRef, caller.getStack()); + method.use(); + for (int i = 0; i < method.getParameterCount(); ++i) { + allClassesNode.connect(method.getVariable(i)); + } + } + private boolean isAssignableFrom(ClassReader supertype, String subtypeName) { + ClassReaderSource classSource = dependencyChecker.getClassSource(); + if (supertype.getName().equals(subtypeName)) { + return true; + } + ClassReader subtype = classSource.get(subtypeName); + if (subtype == null) { + return false; + } + if (isAssignableFrom(supertype, subtype.getParent())) { + return true; + } + for (String iface : subtype.getInterfaces()) { + if (isAssignableFrom(supertype, iface)) { + return true; + } + } + return false; } } } diff --git a/teavm-jso/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java b/teavm-jso/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java index 24449a955..d21f5e80f 100644 --- a/teavm-jso/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java +++ b/teavm-jso/src/main/java/org/teavm/javascript/ni/JSNativeGenerator.java @@ -94,11 +94,11 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin } @Override - public void methodAchieved(final DependencyChecker checker, final MethodDependency graph) { - for (int i = 0; i < graph.getReference().parameterCount(); ++i) { - graph.getVariable(i).addConsumer(new DependencyConsumer() { + public void methodAchieved(final DependencyChecker checker, final MethodDependency method) { + for (int i = 0; i < method.getReference().parameterCount(); ++i) { + method.getVariable(i).addConsumer(new DependencyConsumer() { @Override public void consume(String type) { - achieveFunctorMethods(checker, type, graph); + achieveFunctorMethods(checker, type, method); } }); } @@ -111,7 +111,7 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin ClassReader cls = checker.getClassSource().get(type); if (cls != null) { for (MethodReader method : cls.getMethods()) { - checker.linkMethod(method.getReference(), caller.getStack()); + checker.linkMethod(method.getReference(), caller.getStack()).use(); } } }