From 1fff443c36e6ad73f416759a7b07a699c729d295 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 29 Dec 2014 19:16:29 +0400 Subject: [PATCH] Dependency API supports multiple occurence of reaching events for different location --- .../impl/ClassLookupDependencySupport.java | 8 +- .../classlib/impl/EnumDependencySupport.java | 11 +-- .../impl/NewInstanceDependencySupport.java | 12 +-- .../classlib/impl/ServiceLoaderSupport.java | 17 ++-- .../java/lang/CharacterNativeGenerator.java | 3 +- .../java/lang/ClassNativeGenerator.java | 6 +- .../java/lang/ObjectNativeGenerator.java | 3 +- .../java/lang/StringNativeGenerator.java | 3 +- .../java/lang/SystemNativeGenerator.java | 3 +- .../lang/reflect/ArrayNativeGenerator.java | 3 +- .../java/util/DateNativeGenerator.java | 3 +- .../java/util/TimerNativeGenerator.java | 3 +- .../teavm/callgraph/DefaultCallGraphNode.java | 9 +- .../org/teavm/callgraph/DefaultCallSite.java | 9 ++ .../teavm/dependency/DependencyChecker.java | 87 +++++++++---------- .../teavm/dependency/DependencyListener.java | 8 +- .../teavm/dependency/DependencyPlugin.java | 4 +- .../teavm/dependency/MethodDependency.java | 2 + .../tooling/TestExceptionDependency.java | 7 +- .../org/teavm/html4j/EntryPointGenerator.java | 7 +- .../html4j/JavaScriptBodyDependency.java | 73 +++++++++++----- .../java/org/teavm/jso/JSNativeGenerator.java | 11 ++- .../ResourceAccessorDependencyListener.java | 7 +- 23 files changed, 182 insertions(+), 117 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java index 1950b523e..4d4238300 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java @@ -31,12 +31,12 @@ public class ClassLookupDependencySupport implements DependencyListener { } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { allClasses.propagate(agent.getType(className)); } @Override - public void methodAchieved(final DependencyAgent agent, MethodDependency method) { + public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) { MethodReference ref = method.getReference(); if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) { allClasses.addConsumer(new DependencyConsumer() { @@ -47,7 +47,7 @@ public class ClassLookupDependencySupport implements DependencyListener { } MethodReader initMethod = cls.getMethod(new MethodDescriptor("", ValueType.VOID)); if (initMethod != null) { - agent.linkMethod(initMethod.getReference(), null).use(); + agent.linkMethod(initMethod.getReference(), location).use(); } } }); @@ -55,6 +55,6 @@ public class ClassLookupDependencySupport implements DependencyListener { } @Override - public void fieldAchieved(DependencyAgent dependencyChecker, FieldDependency field) { + public void fieldAchieved(DependencyAgent dependencyChecker, FieldDependency field, CallLocation location) { } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java index edde9b387..190defd64 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java @@ -16,6 +16,7 @@ package org.teavm.classlib.impl; import org.teavm.dependency.*; +import org.teavm.model.CallLocation; import org.teavm.model.ClassReader; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; @@ -35,7 +36,7 @@ public class EnumDependencySupport implements DependencyListener { } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { ClassReader cls = agent.getClassSource().get(className); if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) { return; @@ -45,25 +46,25 @@ public class EnumDependencySupport implements DependencyListener { MethodReader method = cls.getMethod(new MethodDescriptor("values", ValueType.arrayOf(ValueType.object(cls.getName())))); if (method != null) { - agent.linkMethod(method.getReference(), null).use(); + agent.linkMethod(method.getReference(), location).use(); } } } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { if (method.getReference().getClassName().equals("java.lang.Class") && method.getReference().getName().equals("getEnumConstantsImpl")) { unlocked = true; allEnums.connect(method.getResult().getArrayItem()); method.getResult().propagate(agent.getType("[java.lang.Enum")); for (String cls : agent.getAchievableClasses()) { - classAchieved(agent, cls); + classAchieved(agent, cls, location); } } } @Override - public void fieldAchieved(DependencyAgent agent, FieldDependency field) { + public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) { } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java index 82942f31e..e94490899 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java @@ -31,7 +31,7 @@ public class NewInstanceDependencySupport implements DependencyListener { } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { ClassReader cls = agent.getClassSource().get(className); if (cls == null) { return; @@ -46,24 +46,24 @@ public class NewInstanceDependencySupport implements DependencyListener { } @Override - public void methodAchieved(final DependencyAgent agent, MethodDependency method) { + public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) { MethodReader reader = method.getMethod(); if (reader.getOwnerName().equals("java.lang.Class") && reader.getName().equals("newInstance")) { allClassesNode.connect(method.getResult()); method.getResult().addConsumer(new DependencyConsumer() { @Override public void consume(DependencyAgentType type) { - attachConstructor(agent, type.getName()); + attachConstructor(agent, type.getName(), location); } }); } } - private void attachConstructor(DependencyAgent checker, String type) { + private void attachConstructor(DependencyAgent checker, String type, CallLocation location) { MethodReference ref = new MethodReference(type, new MethodDescriptor("", ValueType.VOID)); - checker.linkMethod(ref, null).use(); + checker.linkMethod(ref, location).use(); } @Override - public void fieldAchieved(DependencyAgent dependencyAgent, FieldDependency field) { + public void fieldAchieved(DependencyAgent dependencyAgent, FieldDependency field, CallLocation location) { } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java index bd1451473..d55cf5dbf 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java @@ -25,6 +25,7 @@ import org.teavm.codegen.SourceWriter; import org.teavm.dependency.*; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.CallLocation; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -34,6 +35,7 @@ import org.teavm.model.ValueType; * @author Alexey Andreev */ public class ServiceLoaderSupport implements Generator, DependencyListener { + private Set achievedClasses; private Map> serviceMap = new HashMap<>(); private DependencyNode allClassesNode; private ClassLoader classLoader; @@ -82,7 +84,10 @@ public class ServiceLoaderSupport implements Generator, DependencyListener { } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { + if (!achievedClasses.add(className)) { + return; + } try { Enumeration resources = classLoader.getResources("META-INF/services/" + className); while (resources.hasMoreElements()) { @@ -118,25 +123,25 @@ public class ServiceLoaderSupport implements Generator, DependencyListener { } @Override - public void methodAchieved(final DependencyAgent agent, MethodDependency method) { + public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) { MethodReference ref = method.getReference(); if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) { method.getResult().propagate(agent.getType("[java.lang.Object")); allClassesNode.connect(method.getResult().getArrayItem()); method.getResult().getArrayItem().addConsumer(new DependencyConsumer() { @Override public void consume(DependencyAgentType type) { - initConstructor(agent, type.getName()); + initConstructor(agent, type.getName(), location); } }); } } - private void initConstructor(DependencyAgent agent, String type) { + private void initConstructor(DependencyAgent agent, String type, CallLocation location) { MethodReference ctor = new MethodReference(type, new MethodDescriptor("", ValueType.VOID)); - agent.linkMethod(ctor, null).use(); + agent.linkMethod(ctor, location).use(); } @Override - public void fieldAchieved(DependencyAgent agent, FieldDependency field) { + public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) { } } 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 0aeae2eb8..4031d1cfe 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 @@ -24,6 +24,7 @@ import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; /** @@ -52,7 +53,7 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "obtainDigitMapping": case "obtainClasses": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java index ade1c6f25..6cdca4a5c 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java @@ -186,7 +186,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency graph) { + public void methodAchieved(DependencyAgent agent, MethodDependency graph, CallLocation location) { switch (graph.getReference().getName()) { case "voidClass": case "booleanClass": @@ -205,8 +205,8 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug graph.getResult().propagate(agent.getType("java.lang.Class")); break; case "newInstance": - agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "", - ValueType.VOID), null).use(); + agent.linkMethod(new MethodReference(InstantiationException.class, "", void.class), + location).use(); break; } } 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 f2c2118f6..3176e3319 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 @@ -22,6 +22,7 @@ import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.InjectorContext; +import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; /** @@ -58,7 +59,7 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "clone": method.getVariable(0).connect(method.getResult()); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java index 2dec76c28..c2bb09b26 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java @@ -21,6 +21,7 @@ import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.InjectorContext; +import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; /** @@ -29,7 +30,7 @@ import org.teavm.model.MethodReference; */ public class StringNativeGenerator implements Injector, DependencyPlugin { @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "wrap": method.getVariable(1).connect(method.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 bab9df38a..0bbe21f42 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 @@ -20,6 +20,7 @@ import org.teavm.codegen.SourceWriter; import org.teavm.dependency.*; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.CallLocation; import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; @@ -54,7 +55,7 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "doArrayCopy": achieveArrayCopy(method); 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 e7ca71c98..0f043c3ab 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 @@ -20,6 +20,7 @@ import org.teavm.codegen.SourceWriter; import org.teavm.dependency.*; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.CallLocation; import org.teavm.model.ClassReader; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; @@ -38,7 +39,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN }; @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "getLength": achieveGetLength(agent, method); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java index 8ab1bd5e3..24488481e 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java @@ -22,6 +22,7 @@ import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; /** @@ -70,7 +71,7 @@ public class DateNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getMethod().getName()) { case "toString": case "toLocaleFormat": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java index 27c4a38f8..08ea7f48c 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java @@ -23,6 +23,7 @@ import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; /** @@ -34,7 +35,7 @@ public class TimerNativeGenerator implements Generator, DependencyPlugin { "performOnce", void.class); @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "scheduleOnce": { MethodDependency performMethod = agent.linkMethod(performOnceRef, null); diff --git a/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java b/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java index 951fc8dec..25e4633f5 100644 --- a/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java +++ b/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java @@ -69,16 +69,19 @@ public class DefaultCallGraphNode implements CallGraphNode { return safeCallersCallSites; } - public void addCallSite(MethodReference method, InstructionLocation location) { + public boolean addCallSite(MethodReference method, InstructionLocation location) { DefaultCallGraphNode callee = graph.getNode(method); DefaultCallSite callSite = new DefaultCallSite(location, callee, this); if (callSites.add(callSite)) { callee.callerCallSites.add(callSite); + return true; + } else { + return false; } } - public void addCallSite(MethodReference method) { - addCallSite(method, null); + public boolean addCallSite(MethodReference method) { + return addCallSite(method, null); } @Override diff --git a/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallSite.java b/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallSite.java index badb1d35f..04ea4fe86 100644 --- a/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallSite.java +++ b/teavm-core/src/main/java/org/teavm/callgraph/DefaultCallSite.java @@ -16,6 +16,7 @@ package org.teavm.callgraph; import java.util.Objects; +import org.teavm.model.CallLocation; import org.teavm.model.InstructionLocation; /** @@ -26,11 +27,15 @@ public class DefaultCallSite implements CallSite { private InstructionLocation location; private DefaultCallGraphNode callee; private DefaultCallGraphNode caller; + private CallLocation exactLocation; DefaultCallSite(InstructionLocation location, DefaultCallGraphNode callee, DefaultCallGraphNode caller) { this.location = location; this.callee = callee; this.caller = caller; + if (caller != null) { + exactLocation = new CallLocation(caller.getMethod(), location); + } } @Override @@ -38,6 +43,10 @@ public class DefaultCallSite implements CallSite { return location; } + public CallLocation getExactLocation() { + return exactLocation; + } + @Override public DefaultCallGraphNode getCallee() { return callee; 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 3e17cff61..2fdf098c1 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,6 @@ import org.teavm.callgraph.CallGraph; import org.teavm.callgraph.DefaultCallGraph; import org.teavm.callgraph.DefaultCallGraphNode; import org.teavm.common.CachedMapper; -import org.teavm.common.CachedMapper.KeyListener; import org.teavm.common.Mapper; import org.teavm.common.ServiceRepository; import org.teavm.diagnostics.Diagnostics; @@ -109,37 +108,6 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { return createClassDependency(preimage); } }); - methodCache.addKeyListener(new KeyListener() { - @Override public void keyAdded(MethodReference key) { - MethodDependency graph = methodCache.getKnown(key); - if (!graph.isMissing()) { - for (DependencyListener listener : listeners) { - listener.methodAchieved(DependencyChecker.this, graph); - } - activateDependencyPlugin(graph); - } - } - }); - fieldCache.addKeyListener(new KeyListener() { - @Override public void keyAdded(FieldReference key) { - FieldDependency fieldDep = fieldCache.getKnown(key); - if (!fieldDep.isMissing()) { - for (DependencyListener listener : listeners) { - listener.fieldAchieved(DependencyChecker.this, fieldDep); - } - } - } - }); - classCache.addKeyListener(new KeyListener() { - @Override public void keyAdded(String key) { - ClassDependency classDep = classCache.getKnown(key); - if (!classDep.isMissing()) { - for (DependencyListener listener : listeners) { - listener.classAchieved(DependencyChecker.this, key); - } - } - } - }); } public DependencyCheckerInterruptor getInterruptor() { @@ -234,26 +202,33 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { @Override public ClassDependency linkClass(String className, CallLocation callLocation) { ClassDependency dep = classCache.map(className); + boolean added = true; if (callLocation != null && callLocation.getMethod() != null) { DefaultCallGraphNode callGraphNode = callGraph.getNode(callLocation.getMethod()); - addClassAccess(callGraphNode, className, callLocation.getSourceLocation()); + added = addClassAccess(callGraphNode, className, callLocation.getSourceLocation()); + } + if (!dep.isMissing() && added) { + for (DependencyListener listener : listeners) { + listener.classAchieved(DependencyChecker.this, className, callLocation); + } } return dep; } - private void addClassAccess(DefaultCallGraphNode node, String className, InstructionLocation loc) { + private boolean addClassAccess(DefaultCallGraphNode node, String className, InstructionLocation loc) { if (!node.addClassAccess(className, loc)) { - return; + return false; } ClassReader cls = classSource.get(className); if (cls != null) { if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) { - addClassAccess(node, cls.getParent(), loc); + return addClassAccess(node, cls.getParent(), loc); } for (String iface : cls.getInterfaces()) { - addClassAccess(node, iface, loc); + return addClassAccess(node, iface, loc); } } + return false; } private ClassDependency createClassDependency(String className) { @@ -276,10 +251,19 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { throw new IllegalArgumentException(); } callGraph.getNode(methodRef); + boolean added = true; if (callLocation != null && callLocation.getMethod() != null) { - callGraph.getNode(callLocation.getMethod()).addCallSite(methodRef, callLocation.getSourceLocation()); + added = callGraph.getNode(callLocation.getMethod()).addCallSite(methodRef, + callLocation.getSourceLocation()); } - return methodCache.map(methodRef); + MethodDependency graph = methodCache.map(methodRef); + if (!graph.isMissing() && added) { + for (DependencyListener listener : listeners) { + listener.methodAchieved(this, graph, callLocation); + } + activateDependencyPlugin(graph, callLocation); + } + return graph; } void initClass(ClassDependency cls, final CallLocation callLocation) { @@ -404,8 +388,9 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { @Override public FieldDependency linkField(final FieldReference fieldRef, final CallLocation location) { + boolean added = true; if (location != null) { - callGraph.getNode(location.getMethod()).addFieldAccess(fieldRef, location.getSourceLocation()); + added = callGraph.getNode(location.getMethod()).addFieldAccess(fieldRef, location.getSourceLocation()); } FieldDependency dep = fieldCache.map(fieldRef); if (!dep.isMissing()) { @@ -415,6 +400,11 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { } }); } + if (!dep.isMissing() && added) { + for (DependencyListener listener : listeners) { + listener.fieldAchieved(DependencyChecker.this, dep, location); + } + } return dep; } @@ -444,7 +434,18 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { return dep; } - private void activateDependencyPlugin(MethodDependency methodDep) { + private void activateDependencyPlugin(MethodDependency methodDep, CallLocation location) { + attachDependencyPlugin(methodDep); + if (methodDep.dependencyPlugin != null) { + methodDep.dependencyPlugin.methodAchieved(this, methodDep, location); + } + } + + private void attachDependencyPlugin(MethodDependency methodDep) { + if (methodDep.dependencyPluginAttached) { + return; + } + methodDep.dependencyPluginAttached = true; AnnotationReader depAnnot = methodDep.getMethod().getAnnotations().get(PluggableDependency.class.getName()); if (depAnnot == null) { return; @@ -457,13 +458,11 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { } catch (ClassNotFoundException e) { throw new RuntimeException("Dependency plugin not found: " + depClassName, e); } - DependencyPlugin plugin; try { - plugin = (DependencyPlugin)depClass.newInstance(); + methodDep.dependencyPlugin = (DependencyPlugin)depClass.newInstance(); } catch (IllegalAccessException | InstantiationException e) { throw new RuntimeException("Can't instantiate dependency plugin " + depClassName, e); } - plugin.methodAchieved(this, methodDep); } @Override 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 a662d129d..d7851b3a2 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyListener.java @@ -15,6 +15,8 @@ */ package org.teavm.dependency; +import org.teavm.model.CallLocation; + /** * * @author Alexey Andreev @@ -22,9 +24,9 @@ package org.teavm.dependency; public interface DependencyListener { void started(DependencyAgent agent); - void classAchieved(DependencyAgent agent, String className); + void classAchieved(DependencyAgent agent, String className, CallLocation location); - void methodAchieved(DependencyAgent agent, MethodDependency method); + void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location); - void fieldAchieved(DependencyAgent agent, FieldDependency field); + void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location); } 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 29d650351..63d44035d 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java @@ -15,10 +15,12 @@ */ package org.teavm.dependency; +import org.teavm.model.CallLocation; + /** * * @author Alexey Andreev */ public interface DependencyPlugin { - void methodAchieved(DependencyAgent checker, MethodDependency method); + void methodAchieved(DependencyAgent checker, MethodDependency method, CallLocation location); } 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 29b215206..5b160c2a2 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -32,6 +32,8 @@ public class MethodDependency implements MethodDependencyInfo { private MethodReader method; private MethodReference reference; private boolean used; + DependencyPlugin dependencyPlugin; + boolean dependencyPluginAttached; MethodDependency(DependencyChecker dependencyChecker, DependencyNode[] variableNodes, int parameterCount, DependencyNode resultNode, DependencyNode thrown, MethodReader method, MethodReference reference) { diff --git a/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java b/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java index 3a26b9335..214c9bbe8 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java @@ -16,6 +16,7 @@ package org.teavm.tooling; import org.teavm.dependency.*; +import org.teavm.model.CallLocation; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; @@ -35,7 +36,7 @@ class TestExceptionDependency implements DependencyListener { } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { if (isException(agent.getClassSource(), className)) { allClasses.propagate(agent.getType(className)); } @@ -56,13 +57,13 @@ class TestExceptionDependency implements DependencyListener { } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { if (method.getReference().equals(getMessageRef)) { allClasses.connect(method.getVariable(1)); } } @Override - public void fieldAchieved(DependencyAgent agent, FieldDependency field) { + public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) { } } diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/EntryPointGenerator.java b/teavm-html4j/src/main/java/org/teavm/html4j/EntryPointGenerator.java index cee2fc10d..797ab26f4 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/EntryPointGenerator.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/EntryPointGenerator.java @@ -22,6 +22,7 @@ import org.teavm.codegen.SourceWriter; import org.teavm.dependency.*; import org.teavm.javascript.Renderer; import org.teavm.javascript.RenderingContext; +import org.teavm.model.CallLocation; import org.teavm.vm.BuildTarget; import org.teavm.vm.spi.AbstractRendererListener; @@ -74,14 +75,14 @@ public class EntryPointGenerator extends AbstractRendererListener implements Dep } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { } @Override - public void fieldAchieved(DependencyAgent agent, FieldDependency field) { + public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) { } } 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 b3fea8c92..f77ef54b0 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -15,9 +15,27 @@ */ package org.teavm.html4j; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import net.java.html.js.JavaScriptBody; -import org.teavm.dependency.*; -import org.teavm.model.*; +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyAgentType; +import org.teavm.dependency.DependencyConsumer; +import org.teavm.dependency.DependencyListener; +import org.teavm.dependency.DependencyNode; +import org.teavm.dependency.FieldDependency; +import org.teavm.dependency.MethodDependency; +import org.teavm.model.AnnotationReader; +import org.teavm.model.AnnotationValue; +import org.teavm.model.CallLocation; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.ElementModifier; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; /** * @@ -25,6 +43,7 @@ import org.teavm.model.*; */ public class JavaScriptBodyDependency implements DependencyListener { private DependencyNode allClassesNode; + private Map> achievedMethods = new HashMap<>(); @Override public void started(DependencyAgent agent) { @@ -43,7 +62,7 @@ public class JavaScriptBodyDependency implements DependencyListener { } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { ClassReader cls = agent.getClassSource().get(className); if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT) && !cls.hasModifier(ElementModifier.INTERFACE)) { @@ -52,13 +71,21 @@ public class JavaScriptBodyDependency implements DependencyListener { } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { + Set methodsToAchieve = achievedMethods.get(method.getReference()); + if (methodsToAchieve != null) { + for (MethodReference methodToAchieve : methodsToAchieve) { + agent.linkMethod(methodToAchieve, location); + } + return; + } + achievedMethods.put(method.getReference(), new HashSet()); if (method.isMissing()) { return; } AnnotationReader annot = method.getMethod().getAnnotations().get(JavaScriptBody.class.getName()); if (annot != null) { - includeDefaultDependencies(agent); + includeDefaultDependencies(agent, location); AnnotationValue javacall = annot.getValue("javacall"); if (method.getResult() != null) { allClassesNode.connect(method.getResult()); @@ -74,26 +101,26 @@ public class JavaScriptBodyDependency implements DependencyListener { } if (javacall != null && javacall.getBoolean()) { String body = annot.getValue("body").getString(); - new GeneratorJsCallback(agent, method).parse(body); + new GeneratorJsCallback(agent, method, location).parse(body); } } } - private void includeDefaultDependencies(DependencyAgent agent) { - agent.linkMethod(JavaScriptConvGenerator.fromJsMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.toJsMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.intValueMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.booleanValueMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.doubleValueMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.valueOfDoubleMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.charValueMethod, null).use(); - agent.linkMethod(JavaScriptConvGenerator.valueOfCharMethod, null).use(); + private void includeDefaultDependencies(DependencyAgent agent, CallLocation location) { + agent.linkMethod(JavaScriptConvGenerator.fromJsMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.toJsMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.intValueMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.booleanValueMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.doubleValueMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.valueOfDoubleMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.charValueMethod, location).use(); + agent.linkMethod(JavaScriptConvGenerator.valueOfCharMethod, location).use(); } @Override - public void fieldAchieved(DependencyAgent agent, FieldDependency fieldDep) { + public void fieldAchieved(DependencyAgent agent, FieldDependency fieldDep, CallLocation location) { } private static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) { @@ -126,15 +153,18 @@ public class JavaScriptBodyDependency implements DependencyListener { private class GeneratorJsCallback extends JsCallback { private DependencyAgent agent; private MethodDependency caller; - public GeneratorJsCallback(DependencyAgent agent, MethodDependency caller) { + private CallLocation location; + public GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) { this.agent = agent; this.caller = caller; + this.location = location; } @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) { MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); MethodReader reader = findMethod(agent.getClassSource(), fqn, desc); MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc); - MethodDependency methodDep = agent.linkMethod(ref, null); + MethodDependency methodDep = agent.linkMethod(ref, location); + achievedMethods.get(caller.getReference()).add(ref); if (!methodDep.isMissing()) { if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) { methodDep.use(); @@ -154,8 +184,7 @@ public class JavaScriptBodyDependency implements DependencyListener { private MethodReader superMethod; private ClassReader superClass; private MethodDependency caller; - public VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, - MethodDependency caller) { + public VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) { this.agent = agent; this.superMethod = superMethod; this.caller = caller; diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java index a3e8cd094..ef4f9fb16 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java @@ -23,6 +23,7 @@ import org.teavm.javascript.ast.Expr; import org.teavm.javascript.ast.InvocationExpr; import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.InjectorContext; +import org.teavm.model.CallLocation; import org.teavm.model.ClassReader; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; @@ -112,7 +113,8 @@ public class JSNativeGenerator implements Injector, DependencyPlugin { } @Override - public void methodAchieved(final DependencyAgent agent, final MethodDependency method) { + public void methodAchieved(final DependencyAgent agent, final MethodDependency method, + final CallLocation location) { switch (method.getReference().getName()) { case "invoke": case "instantiate": @@ -120,7 +122,7 @@ public class JSNativeGenerator implements Injector, DependencyPlugin { for (int i = 0; i < method.getReference().parameterCount(); ++i) { method.getVariable(i).addConsumer(new DependencyConsumer() { @Override public void consume(DependencyAgentType type) { - achieveFunctorMethods(agent, type.getName(), method); + achieveFunctorMethods(agent, type.getName(), method, location); } }); } @@ -131,14 +133,15 @@ public class JSNativeGenerator implements Injector, DependencyPlugin { } } - private void achieveFunctorMethods(DependencyAgent agent, String type, MethodDependency caller) { + private void achieveFunctorMethods(DependencyAgent agent, String type, MethodDependency caller, + CallLocation location) { if (caller.isMissing()) { return; } ClassReader cls = agent.getClassSource().get(type); if (cls != null) { for (MethodReader method : cls.getMethods()) { - agent.linkMethod(method.getReference(), null).use(); + agent.linkMethod(method.getReference(), location).use(); } } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java index 627ab413e..0e3462599 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java @@ -19,6 +19,7 @@ import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyListener; import org.teavm.dependency.FieldDependency; import org.teavm.dependency.MethodDependency; +import org.teavm.model.CallLocation; /** * @@ -30,11 +31,11 @@ class ResourceAccessorDependencyListener implements DependencyListener { } @Override - public void classAchieved(DependencyAgent agent, String className) { + public void classAchieved(DependencyAgent agent, String className, CallLocation location) { } @Override - public void methodAchieved(DependencyAgent agent, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { switch (method.getReference().getName()) { case "castToString": method.getResult().propagate(agent.getType("java.lang.String")); @@ -43,6 +44,6 @@ class ResourceAccessorDependencyListener implements DependencyListener { } @Override - public void fieldAchieved(DependencyAgent agent, FieldDependency field) { + public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) { } }