Dependency API supports multiple occurence of reaching events for

different location
This commit is contained in:
Alexey Andreev 2014-12-29 19:16:29 +04:00
parent cd0664d695
commit 1fff443c36
23 changed files with 182 additions and 117 deletions

View File

@ -31,12 +31,12 @@ public class ClassLookupDependencySupport implements DependencyListener {
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
allClasses.propagate(agent.getType(className)); allClasses.propagate(agent.getType(className));
} }
@Override @Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method) { public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) { if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) {
allClasses.addConsumer(new DependencyConsumer() { allClasses.addConsumer(new DependencyConsumer() {
@ -47,7 +47,7 @@ public class ClassLookupDependencySupport implements DependencyListener {
} }
MethodReader initMethod = cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)); MethodReader initMethod = cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID));
if (initMethod != null) { 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 @Override
public void fieldAchieved(DependencyAgent dependencyChecker, FieldDependency field) { public void fieldAchieved(DependencyAgent dependencyChecker, FieldDependency field, CallLocation location) {
} }
} }

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.impl; package org.teavm.classlib.impl;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
@ -35,7 +36,7 @@ public class EnumDependencySupport implements DependencyListener {
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
ClassReader cls = agent.getClassSource().get(className); ClassReader cls = agent.getClassSource().get(className);
if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) { if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) {
return; return;
@ -45,25 +46,25 @@ public class EnumDependencySupport implements DependencyListener {
MethodReader method = cls.getMethod(new MethodDescriptor("values", MethodReader method = cls.getMethod(new MethodDescriptor("values",
ValueType.arrayOf(ValueType.object(cls.getName())))); ValueType.arrayOf(ValueType.object(cls.getName()))));
if (method != null) { if (method != null) {
agent.linkMethod(method.getReference(), null).use(); agent.linkMethod(method.getReference(), location).use();
} }
} }
} }
@Override @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") && if (method.getReference().getClassName().equals("java.lang.Class") &&
method.getReference().getName().equals("getEnumConstantsImpl")) { method.getReference().getName().equals("getEnumConstantsImpl")) {
unlocked = true; unlocked = true;
allEnums.connect(method.getResult().getArrayItem()); allEnums.connect(method.getResult().getArrayItem());
method.getResult().propagate(agent.getType("[java.lang.Enum")); method.getResult().propagate(agent.getType("[java.lang.Enum"));
for (String cls : agent.getAchievableClasses()) { for (String cls : agent.getAchievableClasses()) {
classAchieved(agent, cls); classAchieved(agent, cls, location);
} }
} }
} }
@Override @Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field) { public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
} }
} }

View File

@ -31,7 +31,7 @@ public class NewInstanceDependencySupport implements DependencyListener {
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
ClassReader cls = agent.getClassSource().get(className); ClassReader cls = agent.getClassSource().get(className);
if (cls == null) { if (cls == null) {
return; return;
@ -46,24 +46,24 @@ public class NewInstanceDependencySupport implements DependencyListener {
} }
@Override @Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method) { public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
MethodReader reader = method.getMethod(); MethodReader reader = method.getMethod();
if (reader.getOwnerName().equals("java.lang.Class") && reader.getName().equals("newInstance")) { if (reader.getOwnerName().equals("java.lang.Class") && reader.getName().equals("newInstance")) {
allClassesNode.connect(method.getResult()); allClassesNode.connect(method.getResult());
method.getResult().addConsumer(new DependencyConsumer() { method.getResult().addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) { @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("<init>", ValueType.VOID)); MethodReference ref = new MethodReference(type, new MethodDescriptor("<init>", ValueType.VOID));
checker.linkMethod(ref, null).use(); checker.linkMethod(ref, location).use();
} }
@Override @Override
public void fieldAchieved(DependencyAgent dependencyAgent, FieldDependency field) { public void fieldAchieved(DependencyAgent dependencyAgent, FieldDependency field, CallLocation location) {
} }
} }

View File

@ -25,6 +25,7 @@ import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
@ -34,6 +35,7 @@ import org.teavm.model.ValueType;
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public class ServiceLoaderSupport implements Generator, DependencyListener { public class ServiceLoaderSupport implements Generator, DependencyListener {
private Set<String> achievedClasses;
private Map<String, List<String>> serviceMap = new HashMap<>(); private Map<String, List<String>> serviceMap = new HashMap<>();
private DependencyNode allClassesNode; private DependencyNode allClassesNode;
private ClassLoader classLoader; private ClassLoader classLoader;
@ -82,7 +84,10 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
if (!achievedClasses.add(className)) {
return;
}
try { try {
Enumeration<URL> resources = classLoader.getResources("META-INF/services/" + className); Enumeration<URL> resources = classLoader.getResources("META-INF/services/" + className);
while (resources.hasMoreElements()) { while (resources.hasMoreElements()) {
@ -118,25 +123,25 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
} }
@Override @Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method) { public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) { if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
method.getResult().propagate(agent.getType("[java.lang.Object")); method.getResult().propagate(agent.getType("[java.lang.Object"));
allClassesNode.connect(method.getResult().getArrayItem()); allClassesNode.connect(method.getResult().getArrayItem());
method.getResult().getArrayItem().addConsumer(new DependencyConsumer() { method.getResult().getArrayItem().addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) { @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("<init>", ValueType.VOID)); MethodReference ctor = new MethodReference(type, new MethodDescriptor("<init>", ValueType.VOID));
agent.linkMethod(ctor, null).use(); agent.linkMethod(ctor, location).use();
} }
@Override @Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field) { public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
} }
} }

View File

@ -24,6 +24,7 @@ import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/** /**
@ -52,7 +53,7 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin {
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "obtainDigitMapping": case "obtainDigitMapping":
case "obtainClasses": case "obtainClasses":

View File

@ -186,7 +186,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency graph) { public void methodAchieved(DependencyAgent agent, MethodDependency graph, CallLocation location) {
switch (graph.getReference().getName()) { switch (graph.getReference().getName()) {
case "voidClass": case "voidClass":
case "booleanClass": case "booleanClass":
@ -205,8 +205,8 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
graph.getResult().propagate(agent.getType("java.lang.Class")); graph.getResult().propagate(agent.getType("java.lang.Class"));
break; break;
case "newInstance": case "newInstance":
agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>", agent.linkMethod(new MethodReference(InstantiationException.class, "<init>", void.class),
ValueType.VOID), null).use(); location).use();
break; break;
} }
} }

View File

@ -22,6 +22,7 @@ import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.Injector;
import org.teavm.javascript.ni.InjectorContext; import org.teavm.javascript.ni.InjectorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/** /**
@ -58,7 +59,7 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "clone": case "clone":
method.getVariable(0).connect(method.getResult()); method.getVariable(0).connect(method.getResult());

View File

@ -21,6 +21,7 @@ import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.Injector;
import org.teavm.javascript.ni.InjectorContext; import org.teavm.javascript.ni.InjectorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/** /**
@ -29,7 +30,7 @@ import org.teavm.model.MethodReference;
*/ */
public class StringNativeGenerator implements Injector, DependencyPlugin { public class StringNativeGenerator implements Injector, DependencyPlugin {
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "wrap": case "wrap":
method.getVariable(1).connect(method.getResult()); method.getVariable(1).connect(method.getResult());

View File

@ -20,6 +20,7 @@ import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -54,7 +55,7 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "doArrayCopy": case "doArrayCopy":
achieveArrayCopy(method); achieveArrayCopy(method);

View File

@ -20,6 +20,7 @@ import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; 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 }; ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN };
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "getLength": case "getLength":
achieveGetLength(agent, method); achieveGetLength(agent, method);

View File

@ -22,6 +22,7 @@ import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/** /**
@ -70,7 +71,7 @@ public class DateNativeGenerator implements Generator, DependencyPlugin {
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getMethod().getName()) { switch (method.getMethod().getName()) {
case "toString": case "toString":
case "toLocaleFormat": case "toLocaleFormat":

View File

@ -23,6 +23,7 @@ import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/** /**
@ -34,7 +35,7 @@ public class TimerNativeGenerator implements Generator, DependencyPlugin {
"performOnce", void.class); "performOnce", void.class);
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "scheduleOnce": { case "scheduleOnce": {
MethodDependency performMethod = agent.linkMethod(performOnceRef, null); MethodDependency performMethod = agent.linkMethod(performOnceRef, null);

View File

@ -69,16 +69,19 @@ public class DefaultCallGraphNode implements CallGraphNode {
return safeCallersCallSites; return safeCallersCallSites;
} }
public void addCallSite(MethodReference method, InstructionLocation location) { public boolean addCallSite(MethodReference method, InstructionLocation location) {
DefaultCallGraphNode callee = graph.getNode(method); DefaultCallGraphNode callee = graph.getNode(method);
DefaultCallSite callSite = new DefaultCallSite(location, callee, this); DefaultCallSite callSite = new DefaultCallSite(location, callee, this);
if (callSites.add(callSite)) { if (callSites.add(callSite)) {
callee.callerCallSites.add(callSite); callee.callerCallSites.add(callSite);
return true;
} else {
return false;
} }
} }
public void addCallSite(MethodReference method) { public boolean addCallSite(MethodReference method) {
addCallSite(method, null); return addCallSite(method, null);
} }
@Override @Override

View File

@ -16,6 +16,7 @@
package org.teavm.callgraph; package org.teavm.callgraph;
import java.util.Objects; import java.util.Objects;
import org.teavm.model.CallLocation;
import org.teavm.model.InstructionLocation; import org.teavm.model.InstructionLocation;
/** /**
@ -26,11 +27,15 @@ public class DefaultCallSite implements CallSite {
private InstructionLocation location; private InstructionLocation location;
private DefaultCallGraphNode callee; private DefaultCallGraphNode callee;
private DefaultCallGraphNode caller; private DefaultCallGraphNode caller;
private CallLocation exactLocation;
DefaultCallSite(InstructionLocation location, DefaultCallGraphNode callee, DefaultCallGraphNode caller) { DefaultCallSite(InstructionLocation location, DefaultCallGraphNode callee, DefaultCallGraphNode caller) {
this.location = location; this.location = location;
this.callee = callee; this.callee = callee;
this.caller = caller; this.caller = caller;
if (caller != null) {
exactLocation = new CallLocation(caller.getMethod(), location);
}
} }
@Override @Override
@ -38,6 +43,10 @@ public class DefaultCallSite implements CallSite {
return location; return location;
} }
public CallLocation getExactLocation() {
return exactLocation;
}
@Override @Override
public DefaultCallGraphNode getCallee() { public DefaultCallGraphNode getCallee() {
return callee; return callee;

View File

@ -26,7 +26,6 @@ import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.DefaultCallGraph; import org.teavm.callgraph.DefaultCallGraph;
import org.teavm.callgraph.DefaultCallGraphNode; import org.teavm.callgraph.DefaultCallGraphNode;
import org.teavm.common.CachedMapper; import org.teavm.common.CachedMapper;
import org.teavm.common.CachedMapper.KeyListener;
import org.teavm.common.Mapper; import org.teavm.common.Mapper;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
@ -109,37 +108,6 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
return createClassDependency(preimage); return createClassDependency(preimage);
} }
}); });
methodCache.addKeyListener(new KeyListener<MethodReference>() {
@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<FieldReference>() {
@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<String>() {
@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() { public DependencyCheckerInterruptor getInterruptor() {
@ -234,26 +202,33 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
@Override @Override
public ClassDependency linkClass(String className, CallLocation callLocation) { public ClassDependency linkClass(String className, CallLocation callLocation) {
ClassDependency dep = classCache.map(className); ClassDependency dep = classCache.map(className);
boolean added = true;
if (callLocation != null && callLocation.getMethod() != null) { if (callLocation != null && callLocation.getMethod() != null) {
DefaultCallGraphNode callGraphNode = callGraph.getNode(callLocation.getMethod()); 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; 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)) { if (!node.addClassAccess(className, loc)) {
return; return false;
} }
ClassReader cls = classSource.get(className); ClassReader cls = classSource.get(className);
if (cls != null) { if (cls != null) {
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) { 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()) { for (String iface : cls.getInterfaces()) {
addClassAccess(node, iface, loc); return addClassAccess(node, iface, loc);
} }
} }
return false;
} }
private ClassDependency createClassDependency(String className) { private ClassDependency createClassDependency(String className) {
@ -276,10 +251,19 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
callGraph.getNode(methodRef); callGraph.getNode(methodRef);
boolean added = true;
if (callLocation != null && callLocation.getMethod() != null) { 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) { void initClass(ClassDependency cls, final CallLocation callLocation) {
@ -404,8 +388,9 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
@Override @Override
public FieldDependency linkField(final FieldReference fieldRef, final CallLocation location) { public FieldDependency linkField(final FieldReference fieldRef, final CallLocation location) {
boolean added = true;
if (location != null) { 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); FieldDependency dep = fieldCache.map(fieldRef);
if (!dep.isMissing()) { 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; return dep;
} }
@ -444,7 +434,18 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
return dep; 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()); AnnotationReader depAnnot = methodDep.getMethod().getAnnotations().get(PluggableDependency.class.getName());
if (depAnnot == null) { if (depAnnot == null) {
return; return;
@ -457,13 +458,11 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw new RuntimeException("Dependency plugin not found: " + depClassName, e); throw new RuntimeException("Dependency plugin not found: " + depClassName, e);
} }
DependencyPlugin plugin;
try { try {
plugin = (DependencyPlugin)depClass.newInstance(); methodDep.dependencyPlugin = (DependencyPlugin)depClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) { } catch (IllegalAccessException | InstantiationException e) {
throw new RuntimeException("Can't instantiate dependency plugin " + depClassName, e); throw new RuntimeException("Can't instantiate dependency plugin " + depClassName, e);
} }
plugin.methodAchieved(this, methodDep);
} }
@Override @Override

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import org.teavm.model.CallLocation;
/** /**
* *
* @author Alexey Andreev * @author Alexey Andreev
@ -22,9 +24,9 @@ package org.teavm.dependency;
public interface DependencyListener { public interface DependencyListener {
void started(DependencyAgent agent); 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);
} }

View File

@ -15,10 +15,12 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import org.teavm.model.CallLocation;
/** /**
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public interface DependencyPlugin { public interface DependencyPlugin {
void methodAchieved(DependencyAgent checker, MethodDependency method); void methodAchieved(DependencyAgent checker, MethodDependency method, CallLocation location);
} }

View File

@ -32,6 +32,8 @@ public class MethodDependency implements MethodDependencyInfo {
private MethodReader method; private MethodReader method;
private MethodReference reference; private MethodReference reference;
private boolean used; private boolean used;
DependencyPlugin dependencyPlugin;
boolean dependencyPluginAttached;
MethodDependency(DependencyChecker dependencyChecker, DependencyNode[] variableNodes, int parameterCount, MethodDependency(DependencyChecker dependencyChecker, DependencyNode[] variableNodes, int parameterCount,
DependencyNode resultNode, DependencyNode thrown, MethodReader method, MethodReference reference) { DependencyNode resultNode, DependencyNode thrown, MethodReader method, MethodReference reference) {

View File

@ -16,6 +16,7 @@
package org.teavm.tooling; package org.teavm.tooling;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -35,7 +36,7 @@ class TestExceptionDependency implements DependencyListener {
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
if (isException(agent.getClassSource(), className)) { if (isException(agent.getClassSource(), className)) {
allClasses.propagate(agent.getType(className)); allClasses.propagate(agent.getType(className));
} }
@ -56,13 +57,13 @@ class TestExceptionDependency implements DependencyListener {
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
if (method.getReference().equals(getMessageRef)) { if (method.getReference().equals(getMessageRef)) {
allClasses.connect(method.getVariable(1)); allClasses.connect(method.getVariable(1));
} }
} }
@Override @Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field) { public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
} }
} }

View File

@ -22,6 +22,7 @@ import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.javascript.Renderer; import org.teavm.javascript.Renderer;
import org.teavm.javascript.RenderingContext; import org.teavm.javascript.RenderingContext;
import org.teavm.model.CallLocation;
import org.teavm.vm.BuildTarget; import org.teavm.vm.BuildTarget;
import org.teavm.vm.spi.AbstractRendererListener; import org.teavm.vm.spi.AbstractRendererListener;
@ -74,14 +75,14 @@ public class EntryPointGenerator extends AbstractRendererListener implements Dep
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
} }
@Override @Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field) { public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
} }
} }

View File

@ -15,9 +15,27 @@
*/ */
package org.teavm.html4j; 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 net.java.html.js.JavaScriptBody;
import org.teavm.dependency.*; import org.teavm.dependency.DependencyAgent;
import org.teavm.model.*; 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 { public class JavaScriptBodyDependency implements DependencyListener {
private DependencyNode allClassesNode; private DependencyNode allClassesNode;
private Map<MethodReference, Set<MethodReference>> achievedMethods = new HashMap<>();
@Override @Override
public void started(DependencyAgent agent) { public void started(DependencyAgent agent) {
@ -43,7 +62,7 @@ public class JavaScriptBodyDependency implements DependencyListener {
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
ClassReader cls = agent.getClassSource().get(className); ClassReader cls = agent.getClassSource().get(className);
if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT) && if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT) &&
!cls.hasModifier(ElementModifier.INTERFACE)) { !cls.hasModifier(ElementModifier.INTERFACE)) {
@ -52,13 +71,21 @@ public class JavaScriptBodyDependency implements DependencyListener {
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
Set<MethodReference> methodsToAchieve = achievedMethods.get(method.getReference());
if (methodsToAchieve != null) {
for (MethodReference methodToAchieve : methodsToAchieve) {
agent.linkMethod(methodToAchieve, location);
}
return;
}
achievedMethods.put(method.getReference(), new HashSet<MethodReference>());
if (method.isMissing()) { if (method.isMissing()) {
return; return;
} }
AnnotationReader annot = method.getMethod().getAnnotations().get(JavaScriptBody.class.getName()); AnnotationReader annot = method.getMethod().getAnnotations().get(JavaScriptBody.class.getName());
if (annot != null) { if (annot != null) {
includeDefaultDependencies(agent); includeDefaultDependencies(agent, location);
AnnotationValue javacall = annot.getValue("javacall"); AnnotationValue javacall = annot.getValue("javacall");
if (method.getResult() != null) { if (method.getResult() != null) {
allClassesNode.connect(method.getResult()); allClassesNode.connect(method.getResult());
@ -74,26 +101,26 @@ public class JavaScriptBodyDependency implements DependencyListener {
} }
if (javacall != null && javacall.getBoolean()) { if (javacall != null && javacall.getBoolean()) {
String body = annot.getValue("body").getString(); String body = annot.getValue("body").getString();
new GeneratorJsCallback(agent, method).parse(body); new GeneratorJsCallback(agent, method, location).parse(body);
} }
} }
} }
private void includeDefaultDependencies(DependencyAgent agent) { private void includeDefaultDependencies(DependencyAgent agent, CallLocation location) {
agent.linkMethod(JavaScriptConvGenerator.fromJsMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.fromJsMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.toJsMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.toJsMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.intValueMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.intValueMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.booleanValueMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.booleanValueMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.doubleValueMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.doubleValueMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.valueOfDoubleMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.valueOfDoubleMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.charValueMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.charValueMethod, location).use();
agent.linkMethod(JavaScriptConvGenerator.valueOfCharMethod, null).use(); agent.linkMethod(JavaScriptConvGenerator.valueOfCharMethod, location).use();
} }
@Override @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) { 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 class GeneratorJsCallback extends JsCallback {
private DependencyAgent agent; private DependencyAgent agent;
private MethodDependency caller; private MethodDependency caller;
public GeneratorJsCallback(DependencyAgent agent, MethodDependency caller) { private CallLocation location;
public GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) {
this.agent = agent; this.agent = agent;
this.caller = caller; this.caller = caller;
this.location = location;
} }
@Override protected CharSequence callMethod(String ident, String fqn, String method, String params) { @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) {
MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); MethodDescriptor desc = MethodDescriptor.parse(method + params + "V");
MethodReader reader = findMethod(agent.getClassSource(), fqn, desc); MethodReader reader = findMethod(agent.getClassSource(), fqn, desc);
MethodReference ref = reader != null ? reader.getReference() : new MethodReference(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 (!methodDep.isMissing()) {
if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) { if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) {
methodDep.use(); methodDep.use();
@ -154,8 +184,7 @@ public class JavaScriptBodyDependency implements DependencyListener {
private MethodReader superMethod; private MethodReader superMethod;
private ClassReader superClass; private ClassReader superClass;
private MethodDependency caller; private MethodDependency caller;
public VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, public VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) {
MethodDependency caller) {
this.agent = agent; this.agent = agent;
this.superMethod = superMethod; this.superMethod = superMethod;
this.caller = caller; this.caller = caller;

View File

@ -23,6 +23,7 @@ import org.teavm.javascript.ast.Expr;
import org.teavm.javascript.ast.InvocationExpr; import org.teavm.javascript.ast.InvocationExpr;
import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.Injector;
import org.teavm.javascript.ni.InjectorContext; import org.teavm.javascript.ni.InjectorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -112,7 +113,8 @@ public class JSNativeGenerator implements Injector, DependencyPlugin {
} }
@Override @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()) { switch (method.getReference().getName()) {
case "invoke": case "invoke":
case "instantiate": case "instantiate":
@ -120,7 +122,7 @@ public class JSNativeGenerator implements Injector, DependencyPlugin {
for (int i = 0; i < method.getReference().parameterCount(); ++i) { for (int i = 0; i < method.getReference().parameterCount(); ++i) {
method.getVariable(i).addConsumer(new DependencyConsumer() { method.getVariable(i).addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) { @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()) { if (caller.isMissing()) {
return; return;
} }
ClassReader cls = agent.getClassSource().get(type); ClassReader cls = agent.getClassSource().get(type);
if (cls != null) { if (cls != null) {
for (MethodReader method : cls.getMethods()) { for (MethodReader method : cls.getMethods()) {
agent.linkMethod(method.getReference(), null).use(); agent.linkMethod(method.getReference(), location).use();
} }
} }
} }

View File

@ -19,6 +19,7 @@ import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.FieldDependency; import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.model.CallLocation;
/** /**
* *
@ -30,11 +31,11 @@ class ResourceAccessorDependencyListener implements DependencyListener {
} }
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "castToString": case "castToString":
method.getResult().propagate(agent.getType("java.lang.String")); method.getResult().propagate(agent.getType("java.lang.String"));
@ -43,6 +44,6 @@ class ResourceAccessorDependencyListener implements DependencyListener {
} }
@Override @Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field) { public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
} }
} }