mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Dependency checker complete
This commit is contained in:
parent
7e7cdc5b78
commit
59358dcd8d
|
@ -51,6 +51,7 @@ public class TObject {
|
||||||
public final void wait0(long timeout, int nanos) throws TInterruptedException {
|
public final void wait0(long timeout, int nanos) throws TInterruptedException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
@Rename("wait")
|
@Rename("wait")
|
||||||
public final void wait0() throws TInterruptedException {
|
public final void wait0() throws TInterruptedException {
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,15 @@ package org.teavm.classlibgen;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.teavm.codegen.DefaultAliasProvider;
|
import org.teavm.codegen.DefaultAliasProvider;
|
||||||
import org.teavm.codegen.DefaultNamingStrategy;
|
import org.teavm.codegen.DefaultNamingStrategy;
|
||||||
import org.teavm.codegen.SourceWriter;
|
import org.teavm.codegen.SourceWriter;
|
||||||
|
import org.teavm.dependency.DependencyChecker;
|
||||||
import org.teavm.javascript.Decompiler;
|
import org.teavm.javascript.Decompiler;
|
||||||
import org.teavm.javascript.Renderer;
|
import org.teavm.javascript.Renderer;
|
||||||
import org.teavm.javascript.ast.ClassNode;
|
import org.teavm.javascript.ast.ClassNode;
|
||||||
|
@ -23,6 +28,9 @@ public class ClasslibTestGenerator {
|
||||||
private static DefaultNamingStrategy naming;
|
private static DefaultNamingStrategy naming;
|
||||||
private static SourceWriter writer;
|
private static SourceWriter writer;
|
||||||
private static Renderer renderer;
|
private static Renderer renderer;
|
||||||
|
private static List<MethodReference> testMethods = new ArrayList<>();
|
||||||
|
private static Map<String, List<MethodReference>> groupedMethods = new HashMap<>();
|
||||||
|
private static String[] testClasses = { "java.lang.ObjectTests" };
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
classSource = new ClasspathClassHolderSource();
|
classSource = new ClasspathClassHolderSource();
|
||||||
|
@ -31,23 +39,28 @@ public class ClasslibTestGenerator {
|
||||||
naming = new DefaultNamingStrategy(aliasProvider, classSource);
|
naming = new DefaultNamingStrategy(aliasProvider, classSource);
|
||||||
writer = new SourceWriter(naming);
|
writer = new SourceWriter(naming);
|
||||||
renderer = new Renderer(writer, classSource);
|
renderer = new Renderer(writer, classSource);
|
||||||
decompileClass("java.lang.Object");
|
DependencyChecker dependencyChecker = new DependencyChecker(classSource);
|
||||||
decompileClass("java.lang.ObjectTests");
|
for (String testClass : testClasses) {
|
||||||
decompileClass("java.lang.Class");
|
findTests(classSource.getClassHolder(testClass));
|
||||||
decompileClass("java.lang.annotation.Annotation");
|
}
|
||||||
decompileClass("org.junit.Assert");
|
for (MethodReference methodRef : testMethods) {
|
||||||
decompileClass("org.junit.Test");
|
dependencyChecker.addEntryPoint(methodRef);
|
||||||
|
}
|
||||||
|
dependencyChecker.checkDependencies();
|
||||||
|
for (String className : dependencyChecker.getAchievableClasses()) {
|
||||||
|
decompileClass(className);
|
||||||
|
}
|
||||||
renderHead();
|
renderHead();
|
||||||
ClassLoader classLoader = ClasslibTestGenerator.class.getClassLoader();
|
ClassLoader classLoader = ClasslibTestGenerator.class.getClassLoader();
|
||||||
try (InputStream input = classLoader.getResourceAsStream(
|
try (InputStream input = classLoader.getResourceAsStream("org/teavm/classlib/junit-support.js")) {
|
||||||
"org/teavm/classlib/junit-support.js")) {
|
|
||||||
System.out.println(IOUtils.toString(input));
|
System.out.println(IOUtils.toString(input));
|
||||||
}
|
}
|
||||||
try (InputStream input = classLoader.getResourceAsStream(
|
try (InputStream input = classLoader.getResourceAsStream("org/teavm/javascript/runtime.js")) {
|
||||||
"org/teavm/javascript/runtime.js")) {
|
|
||||||
System.out.println(IOUtils.toString(input));
|
System.out.println(IOUtils.toString(input));
|
||||||
}
|
}
|
||||||
renderClassTest(classSource.getClassHolder("java.lang.ObjectTests"));
|
for (String testClass : testClasses) {
|
||||||
|
renderClassTest(classSource.getClassHolder(testClass));
|
||||||
|
}
|
||||||
System.out.println(writer);
|
System.out.println(writer);
|
||||||
renderFoot();
|
renderFoot();
|
||||||
}
|
}
|
||||||
|
@ -63,8 +76,7 @@ public class ClasslibTestGenerator {
|
||||||
System.out.println("<html>");
|
System.out.println("<html>");
|
||||||
System.out.println(" <head>");
|
System.out.println(" <head>");
|
||||||
System.out.println(" <title>TeaVM JUnit tests</title>");
|
System.out.println(" <title>TeaVM JUnit tests</title>");
|
||||||
System.out.println(" <meta http-equiv=\"Content-Type\" " +
|
System.out.println(" <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\"/>");
|
||||||
"content=\"text/html;charset=UTF-8\"/>");
|
|
||||||
System.out.println(" <title>TeaVM JUnit tests</title>");
|
System.out.println(" <title>TeaVM JUnit tests</title>");
|
||||||
System.out.println(" </head>");
|
System.out.println(" </head>");
|
||||||
System.out.println(" <body>");
|
System.out.println(" <body>");
|
||||||
|
@ -78,16 +90,29 @@ public class ClasslibTestGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void renderClassTest(ClassHolder cls) {
|
private static void renderClassTest(ClassHolder cls) {
|
||||||
|
List<MethodReference> methods = groupedMethods.get(cls.getName());
|
||||||
writer.append("testClass(\"" + cls.getName() + "\", function() {").newLine().indent();
|
writer.append("testClass(\"" + cls.getName() + "\", function() {").newLine().indent();
|
||||||
MethodReference cons = new MethodReference(cls.getName(),
|
MethodReference cons = new MethodReference(cls.getName(), new MethodDescriptor("<init>", ValueType.VOID));
|
||||||
new MethodDescriptor("<init>", ValueType.VOID));
|
for (MethodReference method : methods) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
writer.append("runTestCase(").appendClass(cls.getName()).append(".").appendMethod(cons)
|
||||||
if (method.getAnnotations().get("org.junit.Test") != null) {
|
.append("(), \"" + method.getDescriptor().getName() + "\", \"").appendMethod(method)
|
||||||
MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor());
|
.append("\");").newLine();
|
||||||
writer.append("runTestCase(").appendClass(cls.getName()).append(".").appendMethod(cons)
|
|
||||||
.append("(), \"" + method.getName() + "\", \"").appendMethod(ref).append("\");").newLine();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
writer.outdent().append("})").newLine();
|
writer.outdent().append("})").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void findTests(ClassHolder cls) {
|
||||||
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
|
if (method.getAnnotations().get("org.junit.Test") != null) {
|
||||||
|
MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor());
|
||||||
|
testMethods.add(ref);
|
||||||
|
List<MethodReference> group = groupedMethods.get(cls.getName());
|
||||||
|
if (group == null) {
|
||||||
|
group = new ArrayList<>();
|
||||||
|
groupedMethods.put(cls.getName(), group);
|
||||||
|
}
|
||||||
|
group.add(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class DependencyChecker {
|
||||||
private volatile RuntimeException exceptionOccured;
|
private volatile RuntimeException exceptionOccured;
|
||||||
|
|
||||||
public DependencyChecker(ClassHolderSource classSource) {
|
public DependencyChecker(ClassHolderSource classSource) {
|
||||||
this(classSource, 1);
|
this(classSource, Runtime.getRuntime().availableProcessors());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DependencyChecker(ClassHolderSource classSource, int numThreads) {
|
public DependencyChecker(ClassHolderSource classSource, int numThreads) {
|
||||||
|
@ -103,7 +103,7 @@ public class DependencyChecker {
|
||||||
exceptionOccured = null;
|
exceptionOccured = null;
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
if (executor.awaitTermination(1, TimeUnit.SECONDS)) {
|
if (executor.getActiveCount() == 0 || executor.awaitTermination(1, TimeUnit.SECONDS)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
@ -125,7 +125,7 @@ public class DependencyChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initClass(String className) {
|
private void initClass(String className) {
|
||||||
MethodDescriptor clinitDesc = new MethodDescriptor("<clinit>");
|
MethodDescriptor clinitDesc = new MethodDescriptor("<clinit>", ValueType.VOID);
|
||||||
while (className != null) {
|
while (className != null) {
|
||||||
if (initializedClasses.putIfAbsent(className, clinitDesc) != null) {
|
if (initializedClasses.putIfAbsent(className, clinitDesc) != null) {
|
||||||
break;
|
break;
|
||||||
|
@ -166,6 +166,7 @@ public class DependencyChecker {
|
||||||
MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode, this);
|
MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode, this);
|
||||||
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(this);
|
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(this);
|
||||||
graphBuilder.buildGraph(method, graph);
|
graphBuilder.buildGraph(method, graph);
|
||||||
|
achieveClass(methodRef.getClassName());
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.dependency;
|
package org.teavm.dependency;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
import org.teavm.model.instructions.*;
|
import org.teavm.model.instructions.*;
|
||||||
|
|
||||||
|
@ -25,15 +25,12 @@ import org.teavm.model.instructions.*;
|
||||||
*/
|
*/
|
||||||
class DependencyGraphBuilder {
|
class DependencyGraphBuilder {
|
||||||
private DependencyChecker dependencyChecker;
|
private DependencyChecker dependencyChecker;
|
||||||
private ClassHolderSource classSource;
|
|
||||||
private DependencyNode[] nodes;
|
private DependencyNode[] nodes;
|
||||||
private DependencyNode resultNode;
|
private DependencyNode resultNode;
|
||||||
private Program program;
|
private Program program;
|
||||||
private ValueType resultType;
|
|
||||||
|
|
||||||
public DependencyGraphBuilder(DependencyChecker dependencyChecker) {
|
public DependencyGraphBuilder(DependencyChecker dependencyChecker) {
|
||||||
this.dependencyChecker = dependencyChecker;
|
this.dependencyChecker = dependencyChecker;
|
||||||
this.classSource = dependencyChecker.getClassSource();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void buildGraph(MethodHolder method, MethodGraph graph) {
|
public void buildGraph(MethodHolder method, MethodGraph graph) {
|
||||||
|
@ -41,7 +38,6 @@ class DependencyGraphBuilder {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
program = method.getProgram();
|
program = method.getProgram();
|
||||||
resultType = method.getResultType();
|
|
||||||
resultNode = graph.getResultNode();
|
resultNode = graph.getResultNode();
|
||||||
nodes = graph.getVariableNodes();
|
nodes = graph.getVariableNodes();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
@ -57,38 +53,27 @@ class DependencyGraphBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasBody(MethodHolder method) {
|
|
||||||
Set<ElementModifier> modifiers = method.getModifiers();
|
|
||||||
return !modifiers.contains(ElementModifier.ABSTRACT) &&
|
|
||||||
!modifiers.contains(ElementModifier.NATIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class VirtualCallPropagationListener implements DependencyConsumer {
|
private static class VirtualCallPropagationListener implements DependencyConsumer {
|
||||||
private final DependencyNode node;
|
private final DependencyNode node;
|
||||||
private final MethodDescriptor methodDesc;
|
private final MethodDescriptor methodDesc;
|
||||||
private final DependencyChecker checker;
|
private final DependencyChecker checker;
|
||||||
private final ValueType[] paramTypes;
|
|
||||||
private final DependencyNode[] parameters;
|
private final DependencyNode[] parameters;
|
||||||
private final ValueType resultType;
|
|
||||||
private final DependencyNode result;
|
private final DependencyNode result;
|
||||||
|
|
||||||
public VirtualCallPropagationListener(DependencyNode node, MethodDescriptor methodDesc,
|
public VirtualCallPropagationListener(DependencyNode node, MethodDescriptor methodDesc,
|
||||||
DependencyChecker checker, ValueType[] paramTypes, DependencyNode[] parameters,
|
DependencyChecker checker, DependencyNode[] parameters, DependencyNode result) {
|
||||||
ValueType resultType, DependencyNode result) {
|
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.methodDesc = methodDesc;
|
this.methodDesc = methodDesc;
|
||||||
this.checker = checker;
|
this.checker = checker;
|
||||||
this.paramTypes = paramTypes;
|
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
this.resultType = resultType;
|
|
||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consume(String className) {
|
public void consume(String className) {
|
||||||
if (DependencyChecker.shouldLog) {
|
if (DependencyChecker.shouldLog) {
|
||||||
System.out.println("Virtual call of " + methodDesc + " detected on " +
|
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". " +
|
||||||
node.getTag() + ". Target class is " + className);
|
"Target class is " + className);
|
||||||
}
|
}
|
||||||
MethodReference methodRef = new MethodReference(className, methodDesc);
|
MethodReference methodRef = new MethodReference(className, methodDesc);
|
||||||
MethodHolder method = findMethod(methodRef, checker.getClassSource());
|
MethodHolder method = findMethod(methodRef, checker.getClassSource());
|
||||||
|
@ -122,43 +107,6 @@ class DependencyGraphBuilder {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHolder requireMethod(MethodReference methodRef, ClassHolderSource classSource) {
|
|
||||||
MethodHolder method = findMethod(methodRef, classSource);
|
|
||||||
if (method == null) {
|
|
||||||
throw new IllegalStateException("Method not found: " + methodRef);
|
|
||||||
}
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FieldHolder findField(FieldReference fieldRef, ClassHolderSource classSource) {
|
|
||||||
String className = fieldRef.getClassName();
|
|
||||||
while (className != null) {
|
|
||||||
ClassHolder cls = classSource.getClassHolder(className);
|
|
||||||
if (cls == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
FieldHolder field = cls.getField(fieldRef.getFieldName());
|
|
||||||
if (field != null) {
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
className = cls.getParent();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isPossibleArrayPair(ValueType a, ValueType b) {
|
|
||||||
if (a instanceof ValueType.Array || b instanceof ValueType.Array) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (a == null || b == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (a.toString().equals("Ljava/lang/Object;") && b.toString().equals("Ljava/lang/Object;")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstructionVisitor visitor = new InstructionVisitor() {
|
private InstructionVisitor visitor = new InstructionVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(IsInstanceInstruction insn) {
|
public void visit(IsInstanceInstruction insn) {
|
||||||
|
@ -166,18 +114,74 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvokeInstruction insn) {
|
public void visit(InvokeInstruction insn) {
|
||||||
|
if (insn.getInstance() == null) {
|
||||||
|
invokeSpecial(insn);
|
||||||
|
} else {
|
||||||
|
switch (insn.getType()) {
|
||||||
|
case SPECIAL:
|
||||||
|
invokeSpecial(insn);
|
||||||
|
break;
|
||||||
|
case VIRTUAL:
|
||||||
|
invokeVirtual(insn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeSpecial(InvokeInstruction insn) {
|
||||||
|
MethodReference method = new MethodReference(insn.getClassName(), insn.getMethod());
|
||||||
|
MethodGraph targetGraph = dependencyChecker.attachMethodGraph(method);
|
||||||
|
DependencyNode[] targetParams = targetGraph.getVariableNodes();
|
||||||
|
List<Variable> arguments = insn.getArguments();
|
||||||
|
for (int i = 0; i < arguments.size(); ++i) {
|
||||||
|
nodes[arguments.get(i).getIndex()].connect(targetParams[i + 1]);
|
||||||
|
}
|
||||||
|
if (insn.getInstance() != null) {
|
||||||
|
nodes[insn.getInstance().getIndex()].connect(targetParams[0]);
|
||||||
|
}
|
||||||
|
if (targetGraph.getResultNode() != null) {
|
||||||
|
targetGraph.getResultNode().connect(nodes[insn.getReceiver().getIndex()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeVirtual(InvokeInstruction insn) {
|
||||||
|
List<Variable> arguments = insn.getArguments();
|
||||||
|
DependencyNode[] actualArgs = new DependencyNode[arguments.size() + 1];
|
||||||
|
for (int i = 0; i < arguments.size(); ++i) {
|
||||||
|
actualArgs[i + 1] = nodes[arguments.get(i).getIndex()];
|
||||||
|
}
|
||||||
|
actualArgs[0] = nodes[insn.getInstance().getIndex()];
|
||||||
|
DependencyConsumer listener = new VirtualCallPropagationListener(nodes[insn.getInstance().getIndex()],
|
||||||
|
insn.getMethod(), dependencyChecker, actualArgs,
|
||||||
|
insn.getReceiver() != null ? nodes[insn.getReceiver().getIndex()] : null);
|
||||||
|
nodes[insn.getInstance().getIndex()].addConsumer(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(PutElementInstruction insn) {
|
public void visit(PutElementInstruction insn) {
|
||||||
|
DependencyNode valueNode = nodes[insn.getValue().getIndex()];
|
||||||
|
DependencyNode arrayNode = nodes[insn.getArray().getIndex()];
|
||||||
|
valueNode.connect(arrayNode.getArrayItemNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(GetElementInstruction insn) {
|
public void visit(GetElementInstruction insn) {
|
||||||
|
DependencyNode arrayNode = nodes[insn.getArray().getIndex()];
|
||||||
|
DependencyNode receiverNode = nodes[insn.getReceiver().getIndex()];
|
||||||
|
arrayNode.getArrayItemNode().connect(receiverNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(CloneArrayInstruction insn) {
|
public void visit(CloneArrayInstruction insn) {
|
||||||
|
DependencyNode arrayNode = nodes[insn.getArray().getIndex()];
|
||||||
|
final DependencyNode receiverNode = nodes[insn.getReceiver().getIndex()];
|
||||||
|
arrayNode.addConsumer(new DependencyConsumer() {
|
||||||
|
@Override public void consume(String type) {
|
||||||
|
receiverNode.propagate(type);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
arrayNode.getArrayItemNode().connect(receiverNode.getArrayItemNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -186,22 +190,38 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(PutFieldInstruction insn) {
|
public void visit(PutFieldInstruction insn) {
|
||||||
|
DependencyNode fieldNode = dependencyChecker.getFieldNode(new FieldReference(insn.getClassName(),
|
||||||
|
insn.getField()));
|
||||||
|
DependencyNode valueNode = nodes[insn.getValue().getIndex()];
|
||||||
|
valueNode.connect(fieldNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(GetFieldInstruction insn) {
|
public void visit(GetFieldInstruction insn) {
|
||||||
|
DependencyNode fieldNode = dependencyChecker.getFieldNode(new FieldReference(insn.getClassName(),
|
||||||
|
insn.getField()));
|
||||||
|
DependencyNode receiverNode = nodes[insn.getReceiver().getIndex()];
|
||||||
|
fieldNode.connect(receiverNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructMultiArrayInstruction insn) {
|
public void visit(ConstructMultiArrayInstruction insn) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < insn.getDimensions().size(); ++i) {
|
||||||
|
sb.append('[');
|
||||||
|
}
|
||||||
|
sb.append(insn.getItemType());
|
||||||
|
nodes[insn.getReceiver().getIndex()].propagate(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructInstruction insn) {
|
public void visit(ConstructInstruction insn) {
|
||||||
|
nodes[insn.getReceiver().getIndex()].propagate(insn.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructArrayInstruction insn) {
|
public void visit(ConstructArrayInstruction insn) {
|
||||||
|
nodes[insn.getReceiver().getIndex()].propagate("[" + insn.getItemType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -210,6 +230,9 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ExitInstruction insn) {
|
public void visit(ExitInstruction insn) {
|
||||||
|
if (insn.getValueToReturn() != null) {
|
||||||
|
nodes[insn.getValueToReturn().getIndex()].connect(resultNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -234,10 +257,16 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(CastInstruction insn) {
|
public void visit(CastInstruction insn) {
|
||||||
|
DependencyNode valueNode = nodes[insn.getValue().getIndex()];
|
||||||
|
DependencyNode receiverNode = nodes[insn.getReceiver().getIndex()];
|
||||||
|
valueNode.connect(receiverNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(AssignInstruction insn) {
|
public void visit(AssignInstruction insn) {
|
||||||
|
DependencyNode valueNode = nodes[insn.getAssignee().getIndex()];
|
||||||
|
DependencyNode receiverNode = nodes[insn.getReceiver().getIndex()];
|
||||||
|
valueNode.connect(receiverNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -250,6 +279,7 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(StringConstantInstruction insn) {
|
public void visit(StringConstantInstruction insn) {
|
||||||
|
nodes[insn.getReceiver().getIndex()].propagate("java.lang.String");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -274,6 +304,14 @@ class DependencyGraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ClassConstantInstruction insn) {
|
public void visit(ClassConstantInstruction insn) {
|
||||||
|
nodes[insn.getReceiver().getIndex()].propagate("java.lang.Class");
|
||||||
|
ValueType type = insn.getConstant();
|
||||||
|
while (type instanceof ValueType.Array) {
|
||||||
|
type = ((ValueType.Array)type).getItemType();
|
||||||
|
}
|
||||||
|
if (type instanceof ValueType.Object) {
|
||||||
|
dependencyChecker.achieveClass(((ValueType.Object)type).getClassName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -57,13 +57,17 @@ public class DependencyNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(DependencyNode node) {
|
public void connect(DependencyNode node, DependencyTypeFilter filter) {
|
||||||
DependencyNodeToNodeTransition transition = new DependencyNodeToNodeTransition(this, node);
|
DependencyNodeToNodeTransition transition = new DependencyNodeToNodeTransition(this, node, filter);
|
||||||
if (transitions.putIfAbsent(node, transition) == null) {
|
if (transitions.putIfAbsent(node, transition) == null) {
|
||||||
addConsumer(transition);
|
addConsumer(transition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void connect(DependencyNode node) {
|
||||||
|
connect(node, null);
|
||||||
|
}
|
||||||
|
|
||||||
public DependencyNode getArrayItemNode() {
|
public DependencyNode getArrayItemNode() {
|
||||||
DependencyNode result = arrayItemNode.get();
|
DependencyNode result = arrayItemNode.get();
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
@ -87,6 +91,10 @@ public class DependencyNode {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasArrayType() {
|
||||||
|
return arrayItemNode.get() != null && !arrayItemNode.get().types.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasType(String type) {
|
public boolean hasType(String type) {
|
||||||
return types.containsKey(type);
|
return types.containsKey(type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,20 @@ package org.teavm.dependency;
|
||||||
class DependencyNodeToNodeTransition implements DependencyConsumer {
|
class DependencyNodeToNodeTransition implements DependencyConsumer {
|
||||||
private DependencyNode source;
|
private DependencyNode source;
|
||||||
private DependencyNode destination;
|
private DependencyNode destination;
|
||||||
|
private DependencyTypeFilter filter;
|
||||||
|
|
||||||
public DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination) {
|
public DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination,
|
||||||
|
DependencyTypeFilter filter) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
|
this.filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consume(String type) {
|
public void consume(String type) {
|
||||||
|
if (filter != null && !filter.match(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!destination.hasType(type)) {
|
if (!destination.hasType(type)) {
|
||||||
destination.propagate(type);
|
destination.propagate(type);
|
||||||
if (type.startsWith("[")) {
|
if (type.startsWith("[")) {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.teavm.dependency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public interface DependencyTypeFilter {
|
||||||
|
boolean match(String type);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user