When missing some classes/methods/fields dependency checker throws

exception after checking, providing the full list of missing items.
This commit is contained in:
konsoletyper 2014-02-17 21:02:47 +04:00
parent fa1f8f1c6e
commit 53e1ea3627
3 changed files with 77 additions and 39 deletions

View File

@ -15,10 +15,7 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import org.teavm.common.*; import org.teavm.common.*;
@ -41,6 +38,9 @@ public class DependencyChecker implements DependencyInformation {
private ConcurrentMap<String, Object> achievableClasses = new ConcurrentHashMap<>(); private ConcurrentMap<String, Object> achievableClasses = new ConcurrentHashMap<>();
private ConcurrentMap<String, Object> initializedClasses = new ConcurrentHashMap<>(); private ConcurrentMap<String, Object> initializedClasses = new ConcurrentHashMap<>();
private List<DependencyListener> listeners = new ArrayList<>(); private List<DependencyListener> listeners = new ArrayList<>();
Set<MethodReference> missingMethods = new HashSet<>();
Set<String> missingClasses = new HashSet<>();
Set<FieldReference> missingFields = new HashSet<>();
public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader) { public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader) {
this(classSource, classLoader, new SimpleFiniteExecutor()); this(classSource, classLoader, new SimpleFiniteExecutor());
@ -144,7 +144,8 @@ public class DependencyChecker implements DependencyInformation {
achieveInterfaces(className); achieveInterfaces(className);
ClassHolder cls = classSource.get(className); ClassHolder cls = classSource.get(className);
if (cls == null) { if (cls == null) {
throw new RuntimeException("Class not found: " + className); missingClasses.add(className);
return;
} }
if (cls.getMethod(clinitDesc) != null) { if (cls.getMethod(clinitDesc) != null) {
attachMethodGraph(new MethodReference(className, clinitDesc)); attachMethodGraph(new MethodReference(className, clinitDesc));
@ -156,7 +157,8 @@ public class DependencyChecker implements DependencyInformation {
private void achieveInterfaces(String className) { private void achieveInterfaces(String className) {
ClassHolder cls = classSource.get(className); ClassHolder cls = classSource.get(className);
if (cls == null) { if (cls == null) {
throw new RuntimeException("Class not found: " + className); missingClasses.add(className);
return;
} }
for (String iface : cls.getInterfaces()) { for (String iface : cls.getInterfaces()) {
if (achieveClass(iface)) { if (achieveClass(iface)) {
@ -168,7 +170,12 @@ public class DependencyChecker implements DependencyInformation {
private MethodGraph createMethodGraph(final MethodReference methodRef) { private MethodGraph createMethodGraph(final MethodReference methodRef) {
initClass(methodRef.getClassName()); initClass(methodRef.getClassName());
ClassHolder cls = classSource.get(methodRef.getClassName()); ClassHolder cls = classSource.get(methodRef.getClassName());
MethodHolder method = cls.getMethod(methodRef.getDescriptor()); MethodHolder method;
if (cls == null) {
missingClasses.add(methodRef.getClassName());
method = null;
} else {
method = cls.getMethod(methodRef.getDescriptor());
if (method == null) { if (method == null) {
while (cls != null) { while (cls != null) {
method = cls.getMethod(methodRef.getDescriptor()); method = cls.getMethod(methodRef.getDescriptor());
@ -177,28 +184,30 @@ public class DependencyChecker implements DependencyInformation {
} }
cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null; cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null;
} }
throw new RuntimeException("Method not found: " + methodRef); missingMethods.add(methodRef);
} }
ValueType[] arguments = method.getParameterTypes(); }
ValueType[] arguments = methodRef.getParameterTypes();
int paramCount = arguments.length + 1; int paramCount = arguments.length + 1;
int varCount = Math.max(paramCount, method.getProgram().variableCount()); int varCount = Math.max(paramCount, method != null ? method.getProgram().variableCount() : 0);
DependencyNode[] parameterNodes = new DependencyNode[varCount]; DependencyNode[] parameterNodes = new DependencyNode[varCount];
for (int i = 0; i < varCount; ++i) { for (int i = 0; i < varCount; ++i) {
parameterNodes[i] = new DependencyNode(this); parameterNodes[i] = new DependencyNode(this);
if (shouldLog) { if (shouldLog) {
parameterNodes[i].setTag(method.getOwnerName() + "#" + method.getDescriptor() + ":" + i); parameterNodes[i].setTag(methodRef + ":" + i);
} }
} }
DependencyNode resultNode; DependencyNode resultNode;
if (method.getResultType() == ValueType.VOID) { if (methodRef.getDescriptor().getResultType() == ValueType.VOID) {
resultNode = null; resultNode = null;
} else { } else {
resultNode = new DependencyNode(this); resultNode = new DependencyNode(this);
if (shouldLog) { if (shouldLog) {
resultNode.setTag(method.getOwnerName() + "#" + method.getDescriptor() + ":RESULT"); resultNode.setTag(methodRef + ":RESULT");
} }
} }
final MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode); final MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode);
if (method != null) {
final MethodHolder currentMethod = method; final MethodHolder currentMethod = method;
executor.execute(new Runnable() { executor.execute(new Runnable() {
@Override public void run() { @Override public void run() {
@ -206,6 +215,7 @@ public class DependencyChecker implements DependencyInformation {
graphBuilder.buildGraph(currentMethod, graph); graphBuilder.buildGraph(currentMethod, graph);
} }
}); });
}
return graph; return graph;
} }
@ -241,8 +251,8 @@ public class DependencyChecker implements DependencyInformation {
initClass(fieldRef.getClassName()); initClass(fieldRef.getClassName());
ClassHolder cls = classSource.get(fieldRef.getClassName()); ClassHolder cls = classSource.get(fieldRef.getClassName());
if (cls == null) { if (cls == null) {
throw new RuntimeException("Class not found: " + fieldRef.getClassName()); missingClasses.add(fieldRef.getClassName());
} } else {
FieldHolder field = cls.getField(fieldRef.getFieldName()); FieldHolder field = cls.getField(fieldRef.getFieldName());
if (field == null) { if (field == null) {
while (cls != null) { while (cls != null) {
@ -252,7 +262,8 @@ public class DependencyChecker implements DependencyInformation {
} }
cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null; cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null;
} }
throw new RuntimeException("Field not found: " + fieldRef); missingFields.add(fieldRef);
}
} }
DependencyNode node = new DependencyNode(this); DependencyNode node = new DependencyNode(this);
if (shouldLog) { if (shouldLog) {
@ -263,6 +274,9 @@ public class DependencyChecker implements DependencyInformation {
private void activateDependencyPlugin(MethodReference methodRef) { private void activateDependencyPlugin(MethodReference methodRef) {
ClassHolder cls = classSource.get(methodRef.getClassName()); ClassHolder cls = classSource.get(methodRef.getClassName());
if (cls == null) {
return;
}
MethodHolder method = cls.getMethod(methodRef.getDescriptor()); MethodHolder method = cls.getMethod(methodRef.getDescriptor());
if (method == null) { if (method == null) {
return; return;
@ -336,4 +350,30 @@ public class DependencyChecker implements DependencyInformation {
public DependencyMethodInformation getMethod(MethodReference methodRef) { public DependencyMethodInformation getMethod(MethodReference methodRef) {
return methodCache.getKnown(methodRef); return methodCache.getKnown(methodRef);
} }
public void checkForMissingItems() {
if (missingClasses.isEmpty() && missingMethods.isEmpty() && missingFields.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder();
if (!missingClasses.isEmpty()) {
sb.append("Missing classes:\n");
for (String cls : missingClasses) {
sb.append(" ").append(cls).append("\n");
}
}
if (!missingMethods.isEmpty()) {
sb.append("Missing methods:\n");
for (MethodReference method : missingMethods) {
sb.append(" ").append(method).append("\n");
}
}
if (!missingMethods.isEmpty()) {
sb.append("Missing fields:\n");
for (FieldReference field : missingFields) {
sb.append(" ").append(field).append("\n");
}
}
throw new IllegalStateException(sb.toString());
}
} }

View File

@ -89,9 +89,6 @@ class DependencyGraphBuilder {
return; return;
} }
MethodGraph targetGraph = checker.attachMethodGraph(methodRef); MethodGraph targetGraph = checker.attachMethodGraph(methodRef);
if (targetGraph == null) {
throw new RuntimeException("Method not found: " + methodRef);
}
DependencyNode[] targetParams = targetGraph.getVariables(); DependencyNode[] targetParams = targetGraph.getVariables();
for (int i = 0; i < parameters.length; ++i) { for (int i = 0; i < parameters.length; ++i) {
parameters[i].connect(targetParams[i]); parameters[i].connect(targetParams[i]);

View File

@ -121,6 +121,7 @@ public class JavascriptBuilder implements JavascriptBuilderHost {
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("<init>", dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("<init>",
ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)));
executor.complete(); executor.complete();
dependencyChecker.checkForMissingItems();
ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(); ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses();
Decompiler decompiler = new Decompiler(classSet, classLoader, executor); Decompiler decompiler = new Decompiler(classSet, classLoader, executor);
devirtualize(classSet, dependencyChecker); devirtualize(classSet, dependencyChecker);