Working on dependency checker

This commit is contained in:
Alexey Andreev 2013-11-15 17:31:11 +04:00
parent 93798a335c
commit 7e7cdc5b78
7 changed files with 73 additions and 346 deletions

View File

@ -51,7 +51,6 @@ 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 {
} }

View File

@ -71,16 +71,16 @@ public class DependencyChecker {
} }
MethodGraph graph = attachMethodGraph(methodRef); MethodGraph graph = attachMethodGraph(methodRef);
DependencyNode[] varNodes = graph.getVariableNodes(); DependencyNode[] varNodes = graph.getVariableNodes();
schedulePropagation(varNodes[0], methodRef.getClassName()); varNodes[0].propagate(methodRef.getClassName());
for (int i = 0; i < argumentTypes.length; ++i) { for (int i = 0; i < argumentTypes.length; ++i) {
schedulePropagation(varNodes[i + 1], argumentTypes[i]); varNodes[i + 1].propagate(argumentTypes[i]);
} }
} }
public void schedulePropagation(final DependencyConsumer targetNode, final String type) { public void schedulePropagation(final DependencyConsumer consumer, final String type) {
schedule(new Runnable() { schedule(new Runnable() {
@Override public void run() { @Override public void run() {
targetNode.propagate(type); consumer.consume(type);
} }
}); });
} }

View File

@ -5,7 +5,5 @@ package org.teavm.dependency;
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public interface DependencyConsumer { public interface DependencyConsumer {
void propagate(String type); void consume(String type);
boolean hasType(String type);
} }

View File

@ -30,7 +30,6 @@ class DependencyGraphBuilder {
private DependencyNode resultNode; private DependencyNode resultNode;
private Program program; private Program program;
private ValueType resultType; private ValueType resultType;
private TypeAnalyzer typeAnalyzer;
public DependencyGraphBuilder(DependencyChecker dependencyChecker) { public DependencyGraphBuilder(DependencyChecker dependencyChecker) {
this.dependencyChecker = dependencyChecker; this.dependencyChecker = dependencyChecker;
@ -86,7 +85,7 @@ class DependencyGraphBuilder {
} }
@Override @Override
public void propagate(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() + ". Target class is " + className); node.getTag() + ". Target class is " + className);
@ -100,22 +99,11 @@ class DependencyGraphBuilder {
DependencyNode[] targetParams = targetGraph.getVariableNodes(); DependencyNode[] targetParams = targetGraph.getVariableNodes();
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]);
if (hasBody(method)) {// && isPossibleArrayPair(paramTypes[i], method.getProgram().variableAt(i))) {
targetParams[i].connect(parameters[i]);
}
} }
if (targetGraph.getResultNode() != null) { if (targetGraph.getResultNode() != null) {
targetGraph.getResultNode().connect(result); targetGraph.getResultNode().connect(result);
if (isPossibleArrayPair(method.getResultType(), resultType)) {
result.connect(targetGraph.getResultNode());
}
} }
} }
@Override
public boolean hasType(String type) {
return false;
}
} }
private static MethodHolder findMethod(MethodReference methodRef, ClassHolderSource classSource) { private static MethodHolder findMethod(MethodReference methodRef, ClassHolderSource classSource) {

View File

@ -17,47 +17,76 @@ package org.teavm.dependency;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class DependencyNode implements DependencyConsumer { public class DependencyNode {
private DependencyChecker dependencyChecker; private DependencyChecker dependencyChecker;
private static final Object mapValue = new Object(); private static final Object mapValue = new Object();
private ConcurrentMap<DependencyConsumer, Object> followers = new ConcurrentHashMap<>(); private ConcurrentMap<DependencyConsumer, Object> followers = new ConcurrentHashMap<>();
private ConcurrentMap<String, Object> types = new ConcurrentHashMap<>(); private ConcurrentMap<String, Object> types = new ConcurrentHashMap<>();
private ConcurrentMap<DependencyNode, DependencyNodeToNodeTransition> transitions = new ConcurrentHashMap<>();
private volatile String tag; private volatile String tag;
private final AtomicReference<DependencyNode> arrayItemNode = new AtomicReference<>();
private volatile CountDownLatch arrayItemNodeLatch = new CountDownLatch(1);
DependencyNode(DependencyChecker dependencyChecker) { DependencyNode(DependencyChecker dependencyChecker) {
this.dependencyChecker = dependencyChecker; this.dependencyChecker = dependencyChecker;
} }
@Override
public void propagate(String type) { public void propagate(String type) {
if (types.putIfAbsent(type, mapValue) == null) { if (types.putIfAbsent(type, mapValue) == null) {
if (DependencyChecker.shouldLog) { if (DependencyChecker.shouldLog) {
System.out.println(tag + " -> " + type); System.out.println(tag + " -> " + type);
} }
for (DependencyConsumer follower : followers.keySet().toArray(new DependencyConsumer[0])) { for (DependencyConsumer consumer : followers.keySet().toArray(new DependencyConsumer[0])) {
if (follower.hasType(type)) { dependencyChecker.schedulePropagation(consumer, type);
dependencyChecker.schedulePropagation(follower, type);
}
} }
} }
} }
public void connect(DependencyConsumer follower) { public void addConsumer(DependencyConsumer consumer) {
if (followers.putIfAbsent(follower, mapValue) == null) { if (followers.putIfAbsent(consumer, mapValue) == null) {
for (String type : types.keySet().toArray(new String[0])) { for (String type : types.keySet().toArray(new String[0])) {
if (follower.hasType(type)) { dependencyChecker.schedulePropagation(consumer, type);
dependencyChecker.schedulePropagation(follower, type);
}
} }
} }
} }
@Override public void connect(DependencyNode node) {
DependencyNodeToNodeTransition transition = new DependencyNodeToNodeTransition(this, node);
if (transitions.putIfAbsent(node, transition) == null) {
addConsumer(transition);
}
}
public DependencyNode getArrayItemNode() {
DependencyNode result = arrayItemNode.get();
if (result == null) {
result = new DependencyNode(dependencyChecker);
if (arrayItemNode.compareAndSet(null, result)) {
arrayItemNodeLatch.countDown();
arrayItemNodeLatch = null;
} else {
CountDownLatch latch = arrayItemNodeLatch;
if (latch != null) {
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return result;
}
}
result = arrayItemNode.get();
}
}
return result;
}
public boolean hasType(String type) { public boolean hasType(String type) {
return types.containsKey(type); return types.containsKey(type);
} }

View File

@ -0,0 +1,26 @@
package org.teavm.dependency;
/**
*
* @author Alexey Andreev
*/
class DependencyNodeToNodeTransition implements DependencyConsumer {
private DependencyNode source;
private DependencyNode destination;
public DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination) {
this.source = source;
this.destination = destination;
}
@Override
public void consume(String type) {
if (!destination.hasType(type)) {
destination.propagate(type);
if (type.startsWith("[")) {
source.getArrayItemNode().connect(destination.getArrayItemNode());
destination.getArrayItemNode().connect(destination.getArrayItemNode());
}
}
}
}

View File

@ -1,313 +0,0 @@
package org.teavm.dependency;
import java.util.HashSet;
import java.util.Set;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
class TypeAnalyzer implements InstructionVisitor {
private ClassHolderSource classSource;
private ValueType[] types;
public TypeAnalyzer(ClassHolderSource classSource, int variableCount) {
this.classSource = classSource;
types = new ValueType[variableCount];
}
@Override
public void visit(EmptyInstruction insn) {
}
public ValueType typeOf(int variable) {
return types[variable];
}
public void define(Variable var, ValueType type) {
types[var.getIndex()] = type;
}
public void merge(Variable var, ValueType type) {
if (types[var.getIndex()] == null) {
define(var, type);
} else {
define(var, merge(typeOf(var.getIndex()), type));
}
}
private ValueType merge(ValueType a, ValueType b) {
if (a instanceof ValueType.Array && b instanceof ValueType.Array) {
return merge(((ValueType.Array)a).getItemType(), ((ValueType.Array)b).getItemType());
} else if (a instanceof ValueType.Object && b instanceof ValueType.Object) {
String p = ((ValueType.Object)a).getClassName();
String q = ((ValueType.Object)b).getClassName();
if (p.equals(q)) {
return a;
}
ClassHolder firstClass = classSource.getClassHolder(p);
ClassHolder secondClass = classSource.getClassHolder(q);
if (firstClass.getModifiers().contains(ElementModifier.INTERFACE) ||
secondClass.getModifiers().contains(ElementModifier.INTERFACE)) {
return ValueType.object("java.lang.Object");
}
if (isSuper(secondClass, firstClass)) {
return ValueType.object(secondClass.getName());
}
Set<String> path = getPathToRoot(firstClass);
return ValueType.object(findAmoungSupertypes(secondClass, path));
} else {
return ValueType.object("java.lang.Object");
}
}
private Set<String> getPathToRoot(ClassHolder cls) {
Set<String> path = new HashSet<>();
while (cls != null) {
path.add(cls.getName());
cls = cls.getParent() != null ? classSource.getClassHolder(cls.getParent()) : null;
}
return path;
}
private boolean isSuper(ClassHolder cls, ClassHolder superCls) {
while (cls != null) {
if (cls == superCls) {
return true;
}
cls = cls.getParent() != null ? classSource.getClassHolder(cls.getParent()) : null;
}
return false;
}
private String findAmoungSupertypes(ClassHolder cls, Set<String> supertypes) {
while (cls != null) {
if (supertypes.contains(cls.getName())) {
return cls.getName();
}
cls = cls.getParent() != null ? classSource.getClassHolder(cls.getParent()) : null;
}
return "java.lang.Object";
}
@Override
public void visit(ClassConstantInstruction insn) {
define(insn.getReceiver(), ValueType.object("java.lang.Class"));
}
@Override
public void visit(NullConstantInstruction insn) {
define(insn.getReceiver(), ValueType.NULL);
}
@Override
public void visit(IntegerConstantInstruction insn) {
define(insn.getReceiver(), ValueType.INTEGER);
}
@Override
public void visit(LongConstantInstruction insn) {
define(insn.getReceiver(), ValueType.LONG);
}
@Override
public void visit(FloatConstantInstruction insn) {
define(insn.getReceiver(), ValueType.FLOAT);
}
@Override
public void visit(DoubleConstantInstruction insn) {
define(insn.getReceiver(), ValueType.DOUBLE);
}
@Override
public void visit(StringConstantInstruction insn) {
define(insn.getReceiver(), ValueType.object("java.lang.String"));
}
@Override
public void visit(BinaryInstruction insn) {
switch (insn.getOperation()) {
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case MODULO:
case SHIFT_LEFT:
case SHIFT_RIGHT:
case SHIFT_RIGHT_UNSIGNED:
case AND:
case OR:
case XOR:
define(insn.getReceiver(), map(insn.getOperandType()));
break;
case COMPARE:
define(insn.getReceiver(), ValueType.INTEGER);
break;
}
}
private ValueType map(NumericOperandType type) {
switch (type) {
case INT:
return ValueType.INTEGER;
case LONG:
return ValueType.LONG;
case FLOAT:
return ValueType.FLOAT;
case DOUBLE:
return ValueType.DOUBLE;
}
throw new AssertionError("Unknown type: " + type);
}
@Override
public void visit(NegateInstruction insn) {
define(insn.getReceiver(), map(insn.getOperandType()));
}
@Override
public void visit(AssignInstruction insn) {
define(insn.getReceiver(), types[insn.getAssignee().getIndex()]);
}
@Override
public void visit(CastInstruction insn) {
define(insn.getReceiver(), insn.getTargetType());
}
@Override
public void visit(CastNumberInstruction insn) {
define(insn.getReceiver(), map(insn.getTargetType()));
}
@Override
public void visit(BranchingInstruction insn) {
}
@Override
public void visit(BinaryBranchingInstruction insn) {
}
@Override
public void visit(JumpInstruction insn) {
}
@Override
public void visit(SwitchInstruction insn) {
}
@Override
public void visit(ExitInstruction insn) {
}
@Override
public void visit(RaiseInstruction insn) {
}
@Override
public void visit(ConstructArrayInstruction insn) {
define(insn.getReceiver(), ValueType.arrayOf(insn.getItemType()));
}
@Override
public void visit(ConstructInstruction insn) {
define(insn.getReceiver(), ValueType.object(insn.getType()));
}
@Override
public void visit(ConstructMultiArrayInstruction insn) {
ValueType type = insn.getItemType();
for (int i = 0; i < insn.getDimensions().size(); ++i) {
type = ValueType.arrayOf(type);
}
define(insn.getReceiver(), type);
}
@Override
public void visit(GetFieldInstruction insn) {
FieldHolder field = getRealField(new FieldReference(insn.getClassName(), insn.getField()));
if (field == null) {
throw new RuntimeException("Field not found: " + insn.getClassName() + "." + insn.getField());
}
define(insn.getReceiver(), field.getType());
}
private FieldHolder getRealField(FieldReference ref) {
String className = ref.getClassName();
while (className != null) {
ClassHolder cls = classSource.getClassHolder(className);
if (cls == null) {
return null;
}
FieldHolder field = cls.getField(ref.getFieldName());
if (field.getLevel() == AccessLevel.PRIVATE && !className.equals(ref.getClassName())) {
return null;
}
return field;
}
return null;
}
private MethodHolder getRealMethod(MethodReference ref) {
String className = ref.getClassName();
while (className != null) {
ClassHolder cls = classSource.getClassHolder(className);
if (cls == null) {
return null;
}
MethodHolder method = cls.getMethod(ref.getDescriptor());
if (method.getLevel() == AccessLevel.PRIVATE && !className.equals(ref.getClassName())) {
return null;
}
return method;
}
return null;
}
@Override
public void visit(PutFieldInstruction insn) {
}
@Override
public void visit(ArrayLengthInstruction insn) {
define(insn.getReceiver(), ValueType.INTEGER);
}
@Override
public void visit(CloneArrayInstruction insn) {
define(insn.getReceiver(), types[insn.getArray().getIndex()]);
}
@Override
public void visit(GetElementInstruction insn) {
ValueType type = types[insn.getArray().getIndex()];
if (!(type instanceof ValueType.Array)) {
return;
}
ValueType itemType = ((ValueType.Array)type).getItemType();
define(insn.getReceiver(), itemType);
}
@Override
public void visit(PutElementInstruction insn) {
}
@Override
public void visit(InvokeInstruction insn) {
MethodHolder method = getRealMethod(new MethodReference(insn.getClassName(), insn.getMethod()));
if (method == null) {
throw new RuntimeException("Method not found: " + insn.getMethod());
}
if (insn.getMethod().getResultType() != ValueType.VOID) {
define(insn.getReceiver(), insn.getMethod().getResultType());
}
}
@Override
public void visit(IsInstanceInstruction insn) {
define(insn.getReceiver(), ValueType.BOOLEAN);
}
}