From ee28309008489fb5db390a6a0ae46d650e079749 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 18 Feb 2014 14:20:48 +0400 Subject: [PATCH] Adds displaying of full dependency stack for each missing item --- .../java/lang/CharacterNativeGenerator.java | 11 +- .../java/lang/ObjectNativeGenerator.java | 20 ++- .../java/lang/SystemNativeGenerator.java | 9 +- .../lang/reflect/ArrayNativeGenerator.java | 9 +- .../teavm/dependency/DependencyChecker.java | 114 ++++++++++++------ .../dependency/DependencyGraphBuilder.java | 25 ++-- .../teavm/dependency/DependencyListener.java | 5 +- .../teavm/dependency/DependencyPlugin.java | 4 +- .../org/teavm/dependency/DependencyStack.java | 67 ++++++++++ .../org/teavm/dependency/MethodGraph.java | 16 ++- .../teavm/javascript/JavascriptBuilder.java | 11 +- .../html4j/JavaScriptBodyDependency.java | 35 +++--- .../javascript/ni/JSNativeGenerator.java | 11 +- 13 files changed, 230 insertions(+), 107 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DependencyStack.java 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 614fb1d6b..ec929a6ab 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 @@ -21,6 +21,7 @@ import org.teavm.classlib.impl.unicode.UnicodeSupport; import org.teavm.codegen.SourceWriter; import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyPlugin; +import org.teavm.dependency.MethodGraph; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; import org.teavm.model.MethodReference; @@ -48,10 +49,10 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyChecker checker, MethodReference method) { - switch (method.getName()) { + public void methodAchieved(DependencyChecker checker, MethodGraph graph) { + switch (graph.getReference().getName()) { case "obtainDigitMapping": - achieveObtainDigitMapping(checker, method); + achieveObtainDigitMapping(graph); break; } } @@ -71,7 +72,7 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin { .append("\");").softNewLine(); } - private void achieveObtainDigitMapping(DependencyChecker checker, MethodReference method) { - checker.attachMethodGraph(method).getResult().propagate("java.lang.String"); + private void achieveObtainDigitMapping(MethodGraph graph) { + graph.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 1b2c9a466..4b15c4545 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 @@ -61,16 +61,16 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu } @Override - public void methodAchieved(DependencyChecker checker, MethodReference method) { - switch (method.getDescriptor().getName()) { + public void methodAchieved(DependencyChecker checker, MethodGraph graph) { + switch (graph.getReference().getName()) { case "clone": - achieveClone(checker, method); + achieveClone(graph); break; case "getClass": - achieveGetClass(checker, method); + achieveGetClass(checker, graph); break; case "wrap": - achieveWrap(checker, method); + achieveWrap(graph); break; } } @@ -86,12 +86,12 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu writer.append(".constructor)"); } - private void achieveGetClass(DependencyChecker checker, MethodReference method) { + private void achieveGetClass(DependencyChecker checker, MethodGraph graph) { String classClass = "java.lang.Class"; MethodReference initMethod = new MethodReference(classClass, new MethodDescriptor("createNew", ValueType.object(classClass))); checker.addEntryPoint(initMethod); - checker.attachMethodGraph(method).getResult().propagate("java.lang.Class"); + graph.getResult().propagate("java.lang.Class"); } private void generateHashCode(GeneratorContext context, SourceWriter writer) throws IOException { @@ -107,8 +107,7 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu writer.append("return copy;").softNewLine(); } - private void achieveClone(DependencyChecker checker, MethodReference method) { - MethodGraph graph = checker.attachMethodGraph(method); + private void achieveClone(MethodGraph graph) { graph.getVariable(0).connect(graph.getResult()); } @@ -116,8 +115,7 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu context.writeExpr(context.getArgument(0)); } - private void achieveWrap(DependencyChecker checker, MethodReference method) { - MethodGraph graph = checker.attachMethodGraph(method); + private void achieveWrap(MethodGraph graph) { graph.getVariable(1).connect(graph.getResult()); } } 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 65330d66a..bb079b05f 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, MethodReference method) { - switch (method.getName()) { + public void methodAchieved(DependencyChecker checker, MethodGraph graph) { + switch (graph.getReference().getName()) { case "doArrayCopy": - achieveArrayCopy(checker, method); + achieveArrayCopy(graph); break; } } @@ -66,8 +66,7 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin { writer.append("return Long_fromNumber(new Date().getTime());").softNewLine(); } - private void achieveArrayCopy(DependencyChecker checker, MethodReference method) { - MethodGraph graph = checker.attachMethodGraph(method); + private void achieveArrayCopy(MethodGraph graph) { DependencyNode src = graph.getVariable(1); DependencyNode dest = graph.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 0f3e54a8d..1fb4204e4 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, MethodReference method) { - if (method.getName().equals("getLength")) { - achieveGetLength(checker, method); + public void methodAchieved(DependencyChecker checker, MethodGraph graph) { + if (graph.getReference().getName().equals("getLength")) { + achieveGetLength(checker, graph); } } @@ -59,8 +59,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { writer.append("return " + array + ".data.length;").softNewLine(); } - private void achieveGetLength(final DependencyChecker checker, MethodReference methodRef) { - final MethodGraph graph = checker.attachMethodGraph(methodRef); + private void achieveGetLength(final DependencyChecker checker, final MethodGraph graph) { graph.getVariable(1).addConsumer(new DependencyConsumer() { @Override public void consume(String type) { if (!type.startsWith("[")) { 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 6feabed98..4335f1dbc 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -33,14 +33,17 @@ public class DependencyChecker implements DependencyInformation { private ClassLoader classLoader; private FiniteExecutor executor; 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 ConcurrentMap achievableClasses = new ConcurrentHashMap<>(); private ConcurrentMap initializedClasses = new ConcurrentHashMap<>(); private List listeners = new ArrayList<>(); - Set missingMethods = new HashSet<>(); - Set missingClasses = new HashSet<>(); - Set missingFields = new HashSet<>(); + ConcurrentMap missingMethods = new ConcurrentHashMap<>(); + ConcurrentMap missingClasses = new ConcurrentHashMap<>(); + ConcurrentMap missingFields = new ConcurrentHashMap<>(); public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader) { this(classSource, classLoader, new SimpleFiniteExecutor()); @@ -52,26 +55,28 @@ public class DependencyChecker implements DependencyInformation { this.executor = executor; methodCache = new ConcurrentCachedMapper<>(new Mapper() { @Override public MethodGraph map(MethodReference preimage) { - return createMethodGraph(preimage); + return createMethodGraph(preimage, stacks.get(preimage)); } }); fieldCache = new ConcurrentCachedMapper<>(new Mapper() { @Override public DependencyNode map(FieldReference preimage) { - return createFieldNode(preimage); + return createFieldNode(preimage, fieldStacks.get(preimage)); } }); methodCache.addKeyListener(new KeyListener() { @Override public void keyAdded(MethodReference key) { + MethodGraph graph = methodCache.getKnown(key); for (DependencyListener listener : listeners) { - listener.methodAchieved(DependencyChecker.this, key); + listener.methodAchieved(DependencyChecker.this, graph); } - activateDependencyPlugin(key); + activateDependencyPlugin(graph); } }); fieldCache.addKeyListener(new KeyListener() { @Override public void keyAdded(FieldReference key) { + DependencyNode node = fieldCache.getKnown(key); for (DependencyListener listener : listeners) { - listener.fieldAchieved(DependencyChecker.this, key); + listener.fieldAchieved(DependencyChecker.this, key, node); } } }); @@ -100,7 +105,7 @@ public class DependencyChecker implements DependencyInformation { if (parameters.length != argumentTypes.length) { throw new IllegalArgumentException("argumentTypes length does not match the number of method's arguments"); } - MethodGraph graph = attachMethodGraph(methodRef); + MethodGraph graph = attachMethodGraph(methodRef, DependencyStack.ROOT); DependencyNode[] varNodes = graph.getVariables(); varNodes[0].propagate(methodRef.getClassName()); for (int i = 0; i < argumentTypes.length; ++i) { @@ -120,7 +125,8 @@ public class DependencyChecker implements DependencyInformation { return executor; } - boolean achieveClass(String className) { + boolean achieveClass(String className, DependencyStack stack) { + classStacks.putIfAbsent(className, stack); boolean result = achievableClasses.putIfAbsent(className, dummyValue) == null; if (result) { for (DependencyListener listener : listeners) { @@ -130,49 +136,56 @@ public class DependencyChecker implements DependencyInformation { return result; } - public MethodGraph attachMethodGraph(MethodReference methodRef) { + public MethodGraph attachMethodGraph(MethodReference methodRef, DependencyStack stack) { + stacks.putIfAbsent(methodRef, stack); return methodCache.map(methodRef); } - public void initClass(String className) { + public void initClass(String className, DependencyStack stack) { + classStacks.putIfAbsent(className, stack); MethodDescriptor clinitDesc = new MethodDescriptor("", ValueType.VOID); while (className != null) { if (initializedClasses.putIfAbsent(className, clinitDesc) != null) { break; } - achieveClass(className); - achieveInterfaces(className); + achieveClass(className, stack); + achieveInterfaces(className, stack); ClassHolder cls = classSource.get(className); if (cls == null) { - missingClasses.add(className); + missingClasses.put(className, stack); return; } if (cls.getMethod(clinitDesc) != null) { - attachMethodGraph(new MethodReference(className, clinitDesc)); + MethodReference methodRef = new MethodReference(className, clinitDesc); + attachMethodGraph(methodRef, new DependencyStack(methodRef, stack)); } className = cls.getParent(); } } - private void achieveInterfaces(String className) { + private void achieveInterfaces(String className, DependencyStack stack) { + classStacks.putIfAbsent(className, stack); ClassHolder cls = classSource.get(className); if (cls == null) { - missingClasses.add(className); + missingClasses.put(className, stack); return; } for (String iface : cls.getInterfaces()) { - if (achieveClass(iface)) { - achieveInterfaces(iface); + if (achieveClass(iface, stack)) { + achieveInterfaces(iface, stack); } } } - private MethodGraph createMethodGraph(final MethodReference methodRef) { - initClass(methodRef.getClassName()); + private MethodGraph createMethodGraph(final MethodReference methodRef, DependencyStack stack) { + if (stack == null) { + stack = DependencyStack.ROOT; + } + initClass(methodRef.getClassName(), stack); ClassHolder cls = classSource.get(methodRef.getClassName()); MethodHolder method; if (cls == null) { - missingClasses.add(methodRef.getClassName()); + missingClasses.put(methodRef.getClassName(), stack); method = null; } else { method = cls.getMethod(methodRef.getDescriptor()); @@ -180,11 +193,11 @@ public class DependencyChecker implements DependencyInformation { while (cls != null) { method = cls.getMethod(methodRef.getDescriptor()); if (method != null) { - return methodCache.map(new MethodReference(cls.getName(), methodRef.getDescriptor())); + return attachMethodGraph(method.getReference(), stack); } cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null; } - missingMethods.add(methodRef); + missingMethods.put(methodRef, stack); } } ValueType[] arguments = methodRef.getParameterTypes(); @@ -206,7 +219,8 @@ public class DependencyChecker implements DependencyInformation { resultNode.setTag(methodRef + ":RESULT"); } } - final MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode); + stack = new DependencyStack(methodRef, stack); + final MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode, stack, methodRef); if (method != null) { final MethodHolder currentMethod = method; executor.execute(new Runnable() { @@ -242,27 +256,32 @@ public class DependencyChecker implements DependencyInformation { return new HashSet<>(achievableClasses.keySet()); } - @Override - public DependencyNode getField(FieldReference fieldRef) { + public DependencyNode attachFieldNode(FieldReference fieldRef, DependencyStack stack) { + fieldStacks.putIfAbsent(fieldRef, stack); return fieldCache.map(fieldRef); } - private DependencyNode createFieldNode(FieldReference fieldRef) { - initClass(fieldRef.getClassName()); + @Override + public DependencyNode getField(FieldReference fieldRef) { + return fieldCache.getKnown(fieldRef); + } + + private DependencyNode createFieldNode(FieldReference fieldRef, DependencyStack stack) { + initClass(fieldRef.getClassName(), stack); ClassHolder cls = classSource.get(fieldRef.getClassName()); if (cls == null) { - missingClasses.add(fieldRef.getClassName()); + missingClasses.put(fieldRef.getClassName(), stack); } else { FieldHolder field = cls.getField(fieldRef.getFieldName()); if (field == null) { while (cls != null) { field = cls.getField(fieldRef.getFieldName()); if (field != null) { - return fieldCache.map(new FieldReference(cls.getName(), fieldRef.getFieldName())); + return attachFieldNode(new FieldReference(cls.getName(), fieldRef.getFieldName()), stack); } cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null; } - missingFields.add(fieldRef); + missingFields.put(fieldRef, stack); } } DependencyNode node = new DependencyNode(this); @@ -272,7 +291,8 @@ public class DependencyChecker implements DependencyInformation { return node; } - private void activateDependencyPlugin(MethodReference methodRef) { + private void activateDependencyPlugin(MethodGraph graph) { + MethodReference methodRef = graph.getReference(); ClassHolder cls = classSource.get(methodRef.getClassName()); if (cls == null) { return; @@ -299,10 +319,11 @@ public class DependencyChecker implements DependencyInformation { } catch (IllegalAccessException | InstantiationException e) { throw new RuntimeException("Can't instantiate dependency plugin " + depClassName, e); } - plugin.methodAchieved(this, methodRef); + plugin.methodAchieved(this, graph); } - public void addAbstractMethod(MethodReference methodRef) { + public void addAbstractMethod(MethodReference methodRef, DependencyStack stack) { + stacks.putIfAbsent(methodRef, stack); if (abstractMethods.putIfAbsent(methodRef, methodRef) == null) { String className = methodRef.getClassName(); while (className != null) { @@ -356,11 +377,17 @@ public class DependencyChecker implements DependencyInformation { return; } List items = new ArrayList<>(); - items.addAll(missingClasses); - for (MethodReference method : missingMethods) { + Map stackMap = new HashMap<>(); + for (String cls : missingClasses.keySet()) { + stackMap.put(cls, missingClasses.get(cls)); + items.add(cls); + } + for (MethodReference method : missingMethods.keySet()) { + stackMap.put(method.toString(), missingMethods.get(method)); items.add(method.toString()); } - for (FieldReference field : missingFields) { + for (FieldReference field : missingFields.keySet()) { + stackMap.put(field.toString(), missingFields.get(field)); items.add(field.toString()); } Collections.sort(items); @@ -368,6 +395,15 @@ public class DependencyChecker implements DependencyInformation { sb.append("Can't compile due to the following items missing:\n"); for (String item : items) { sb.append(" ").append(item).append("\n"); + DependencyStack stack = stackMap.get(item); + if (stack == null) { + sb.append(" at unknown location\n"); + } + while (stack.getMethod() != null) { + sb.append(" at " + stack.getMethod() + "\n"); + stack = stack.getCause(); + } + sb.append('\n'); } throw new IllegalStateException(sb.toString()); } 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 a1c74e64a..6953835e7 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -29,6 +29,7 @@ class DependencyGraphBuilder { private DependencyNode[] nodes; private DependencyNode resultNode; private Program program; + private DependencyStack callerStack; public DependencyGraphBuilder(DependencyChecker dependencyChecker) { this.dependencyChecker = dependencyChecker; @@ -38,6 +39,7 @@ class DependencyGraphBuilder { if (method.getProgram().basicBlockCount() == 0) { return; } + callerStack = graph.getStack(); program = method.getProgram(); if (DependencyChecker.shouldLog) { System.out.println("Method achieved: " + method.getReference()); @@ -64,14 +66,17 @@ class DependencyGraphBuilder { private final DependencyChecker checker; private final DependencyNode[] parameters; private final DependencyNode result; + private final DependencyStack stack; public VirtualCallPropagationListener(DependencyNode node, MethodDescriptor methodDesc, - DependencyChecker checker, DependencyNode[] parameters, DependencyNode result) { + DependencyChecker checker, DependencyNode[] parameters, DependencyNode result, + DependencyStack stack) { this.node = node; this.methodDesc = methodDesc; this.checker = checker; this.parameters = parameters; this.result = result; + this.stack = stack; } @Override @@ -88,7 +93,7 @@ class DependencyGraphBuilder { if (method == null) { return; } - MethodGraph targetGraph = checker.attachMethodGraph(methodRef); + MethodGraph targetGraph = checker.attachMethodGraph(methodRef, stack); DependencyNode[] targetParams = targetGraph.getVariables(); for (int i = 0; i < parameters.length; ++i) { parameters[i].connect(targetParams[i]); @@ -137,7 +142,7 @@ class DependencyGraphBuilder { } private void invokeSpecial(InvokeInstruction insn) { - MethodGraph targetGraph = dependencyChecker.attachMethodGraph(insn.getMethod()); + MethodGraph targetGraph = dependencyChecker.attachMethodGraph(insn.getMethod(), callerStack); DependencyNode[] targetParams = targetGraph.getVariables(); List arguments = insn.getArguments(); for (int i = 0; i < arguments.size(); ++i) { @@ -160,8 +165,8 @@ class DependencyGraphBuilder { actualArgs[0] = nodes[insn.getInstance().getIndex()]; DependencyConsumer listener = new VirtualCallPropagationListener(nodes[insn.getInstance().getIndex()], insn.getMethod().getDescriptor(), dependencyChecker, actualArgs, - insn.getReceiver() != null ? nodes[insn.getReceiver().getIndex()] : null); - dependencyChecker.addAbstractMethod(insn.getMethod()); + insn.getReceiver() != null ? nodes[insn.getReceiver().getIndex()] : null, callerStack); + dependencyChecker.addAbstractMethod(insn.getMethod(), callerStack); nodes[insn.getInstance().getIndex()].addConsumer(listener); } @@ -205,14 +210,14 @@ class DependencyGraphBuilder { @Override public void visit(PutFieldInstruction insn) { - DependencyNode fieldNode = dependencyChecker.getField(insn.getField()); + DependencyNode fieldNode = dependencyChecker.attachFieldNode(insn.getField(), callerStack); DependencyNode valueNode = nodes[insn.getValue().getIndex()]; valueNode.connect(fieldNode); } @Override public void visit(GetFieldInstruction insn) { - DependencyNode fieldNode = dependencyChecker.getField(insn.getField()); + DependencyNode fieldNode = dependencyChecker.attachFieldNode(insn.getField(), callerStack); DependencyNode receiverNode = nodes[insn.getReceiver().getIndex()]; fieldNode.connect(receiverNode); } @@ -298,7 +303,7 @@ class DependencyGraphBuilder { public void visit(StringConstantInstruction insn) { nodes[insn.getReceiver().getIndex()].propagate("java.lang.String"); dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor( - "", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); + "", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), callerStack); } @Override @@ -329,7 +334,7 @@ class DependencyGraphBuilder { type = ((ValueType.Array)type).getItemType(); } if (type instanceof ValueType.Object) { - dependencyChecker.achieveClass(((ValueType.Object)type).getClassName()); + dependencyChecker.achieveClass(((ValueType.Object)type).getClassName(), callerStack); } } @@ -339,7 +344,7 @@ class DependencyGraphBuilder { @Override public void visit(InitClassInstruction insn) { - dependencyChecker.initClass(insn.getClassName()); + dependencyChecker.initClass(insn.getClassName(), callerStack); } }; } 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 8be4daf19..17f87271f 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java @@ -16,7 +16,6 @@ package org.teavm.dependency; import org.teavm.model.FieldReference; -import org.teavm.model.MethodReference; /** * @@ -27,7 +26,7 @@ public interface DependencyListener { void classAchieved(DependencyChecker dependencyChecker, String className); - void methodAchieved(DependencyChecker dependencyChecker, MethodReference method); + void methodAchieved(DependencyChecker dependencyChecker, MethodGraph method); - void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field); + void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field, DependencyNode node); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java index e3cb81320..50e769c71 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java @@ -15,12 +15,10 @@ */ package org.teavm.dependency; -import org.teavm.model.MethodReference; - /** * * @author Alexey Andreev */ public interface DependencyPlugin { - void methodAchieved(DependencyChecker checker, MethodReference method); + void methodAchieved(DependencyChecker checker, MethodGraph graph); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyStack.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyStack.java new file mode 100644 index 000000000..d7c10b33c --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyStack.java @@ -0,0 +1,67 @@ +/* + * 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.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class DependencyStack { + public static final DependencyStack ROOT = new DependencyStack(); + private MethodReference method; + private DependencyStack cause; + + private DependencyStack() { + } + + public DependencyStack(MethodReference method) { + this(method, ROOT); + } + + public DependencyStack(MethodReference method, DependencyStack cause) { + if (method == null || cause == null) { + throw new IllegalArgumentException("Arguments must not be null"); + } + this.method = method; + this.cause = cause; + } + + public MethodReference getMethod() { + return method; + } + + public DependencyStack getCause() { + return cause; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + DependencyStack stack = this; + while (true) { + if (stack.method == null) { + sb.append(" used by ROOT\n"); + break; + } else { + sb.append(" used by " + stack.method); + stack = stack.cause; + } + } + return sb.toString(); + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java b/teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java index 344e91d6c..42efd403c 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java @@ -16,6 +16,7 @@ package org.teavm.dependency; import java.util.Arrays; +import org.teavm.model.MethodReference; /** * @@ -25,11 +26,16 @@ public class MethodGraph implements DependencyMethodInformation { private DependencyNode[] variableNodes; private int parameterCount; private DependencyNode resultNode; + private DependencyStack stack; + private MethodReference reference; - MethodGraph(DependencyNode[] variableNodes, int parameterCount, DependencyNode resultNode) { + MethodGraph(DependencyNode[] variableNodes, int parameterCount, DependencyNode resultNode, + DependencyStack stack, MethodReference reference) { this.variableNodes = Arrays.copyOf(variableNodes, variableNodes.length); this.parameterCount = parameterCount; this.resultNode = resultNode; + this.stack = stack; + this.reference = reference; } @Override @@ -56,4 +62,12 @@ public class MethodGraph implements DependencyMethodInformation { public DependencyNode getResult() { return resultNode; } + + public DependencyStack getStack() { + return stack; + } + + public MethodReference getReference() { + return reference; + } } 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 4e281b636..b01ebcc8e 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java @@ -22,9 +22,12 @@ import org.teavm.common.FiniteExecutor; import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyInformation; import org.teavm.dependency.DependencyListener; +import org.teavm.dependency.DependencyStack; import org.teavm.javascript.ast.ClassNode; import org.teavm.model.*; -import org.teavm.model.util.*; +import org.teavm.model.util.ListingBuilder; +import org.teavm.model.util.ProgramUtils; +import org.teavm.model.util.RegisterAllocator; import org.teavm.optimization.ClassSetOptimizer; import org.teavm.optimization.Devirtualization; @@ -88,7 +91,7 @@ public class JavascriptBuilder implements JavascriptBuilderHost { "for method " + ref); } JavascriptEntryPoint entryPoint = new JavascriptEntryPoint(name, ref, - dependencyChecker.attachMethodGraph(ref)); + dependencyChecker.attachMethodGraph(ref, DependencyStack.ROOT)); entryPoints.put(name, entryPoint); return entryPoint; } @@ -117,9 +120,9 @@ public class JavascriptBuilder implements JavascriptBuilderHost { builder.setMinified(minifying); SourceWriter sourceWriter = builder.build(writer); dependencyChecker.attachMethodGraph(new MethodReference("java.lang.Class", new MethodDescriptor("createNew", - ValueType.object("java.lang.Class")))); + ValueType.object("java.lang.Class"))), DependencyStack.ROOT); dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("", - ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); + ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), DependencyStack.ROOT); executor.complete(); dependencyChecker.checkForMissingItems(); ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(); 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 02bad2296..d056e635c 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -48,14 +48,13 @@ public class JavaScriptBodyDependency implements DependencyListener { } @Override - public void methodAchieved(DependencyChecker dependencyChecker, MethodReference methodRef) { - ClassHolder cls = dependencyChecker.getClassSource().get(methodRef.getClassName()); - MethodHolder method = cls.getMethod(methodRef.getDescriptor()); + public void methodAchieved(DependencyChecker dependencyChecker, MethodGraph graph) { + ClassHolder cls = dependencyChecker.getClassSource().get(graph.getReference().getClassName()); + MethodHolder method = cls.getMethod(graph.getReference().getDescriptor()); AnnotationReader annot = method.getAnnotations().get(JavaScriptBody.class.getName()); if (annot != null) { includeDefaultDependencies(dependencyChecker); AnnotationValue javacall = annot.getValue("javacall"); - MethodGraph graph = dependencyChecker.attachMethodGraph(methodRef); if (graph.getResult() != null) { allClassesNode.connect(graph.getResult()); allClassesNode.addConsumer(new OneDirectionalConnection(graph.getResult().getArrayItem())); @@ -70,20 +69,20 @@ public class JavaScriptBodyDependency implements DependencyListener { } if (javacall != null && javacall.getBoolean()) { String body = annot.getValue("body").getString(); - new GeneratorJsCallback(dependencyChecker.getClassSource(), dependencyChecker).parse(body); + new GeneratorJsCallback(dependencyChecker.getClassSource(), dependencyChecker, graph).parse(body); } } } private void includeDefaultDependencies(DependencyChecker dependencyChecker) { - dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.fromJsMethod); - dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.toJsMethod); - dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.intValueMethod); - dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.valueOfIntMethod); + dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.fromJsMethod, DependencyStack.ROOT); + dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.toJsMethod, DependencyStack.ROOT); + dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.intValueMethod, DependencyStack.ROOT); + dependencyChecker.attachMethodGraph(JavaScriptConvGenerator.valueOfIntMethod, DependencyStack.ROOT); } @Override - public void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field) { + public void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field, DependencyNode node) { } private static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) { @@ -113,21 +112,25 @@ public class JavaScriptBodyDependency implements DependencyListener { private class GeneratorJsCallback extends JsCallback { private ClassReaderSource classSource; private DependencyChecker dependencyChecker; - public GeneratorJsCallback(ClassReaderSource classSource, DependencyChecker dependencyChecker) { + private MethodGraph caller; + public GeneratorJsCallback(ClassReaderSource classSource, DependencyChecker dependencyChecker, + MethodGraph caller) { this.classSource = classSource; 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) { if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) { - MethodGraph graph = dependencyChecker.attachMethodGraph(reader.getReference()); + MethodGraph graph = dependencyChecker.attachMethodGraph(reader.getReference(), caller.getStack()); for (int i = 0; i <= graph.getParameterCount(); ++i) { allClassesNode.connect(graph.getVariable(i)); } } else { - allClassesNode.addConsumer(new VirtualCallbackConsumer(classSource, dependencyChecker, desc)); + allClassesNode.addConsumer(new VirtualCallbackConsumer(classSource, dependencyChecker, desc, + caller)); } } return ""; @@ -138,16 +141,18 @@ public class JavaScriptBodyDependency implements DependencyListener { private ClassReaderSource classSource; private DependencyChecker dependencyChecker; private MethodDescriptor desc; + private MethodGraph caller; public VirtualCallbackConsumer(ClassReaderSource classSource, DependencyChecker dependencyChecker, - MethodDescriptor desc) { + MethodDescriptor desc, MethodGraph caller) { this.classSource = classSource; this.dependencyChecker = dependencyChecker; this.desc = desc; + this.caller = caller; } @Override public void consume(String type) { MethodReader reader = findMethod(classSource, type, desc); if (reader != null) { - MethodGraph graph = dependencyChecker.attachMethodGraph(reader.getReference()); + MethodGraph graph = dependencyChecker.attachMethodGraph(reader.getReference(), caller.getStack()); for (int i = 0; i < graph.getParameterCount(); ++i) { allClassesNode.connect(graph.getVariable(i)); } 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 3bf126e99..ada8d5390 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 @@ -97,22 +97,21 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin } @Override - public void methodAchieved(final DependencyChecker checker, MethodReference method) { - MethodGraph graph = checker.attachMethodGraph(method); - for (int i = 0; i < method.parameterCount(); ++i) { + public void methodAchieved(final DependencyChecker checker, final MethodGraph graph) { + for (int i = 0; i < graph.getReference().parameterCount(); ++i) { graph.getVariable(i).addConsumer(new DependencyConsumer() { @Override public void consume(String type) { - achieveFunctorMethods(checker, type); + achieveFunctorMethods(checker, type, graph); } }); } } - private void achieveFunctorMethods(DependencyChecker checker, String type) { + private void achieveFunctorMethods(DependencyChecker checker, String type, MethodGraph caller) { ClassHolder cls = checker.getClassSource().get(type); if (cls != null) { for (MethodHolder method : cls.getMethods()) { - checker.attachMethodGraph(method.getReference()); + checker.attachMethodGraph(method.getReference(), caller.getStack()); } } }