diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index dfd3affd6..e60553703 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -51,7 +51,6 @@ public class TObject { public final void wait0(long timeout, int nanos) throws TInterruptedException { } - @SuppressWarnings("unused") @Rename("wait") public final void wait0() throws TInterruptedException { } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 1da3296ab..ac51ccac5 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -71,16 +71,16 @@ public class DependencyChecker { } MethodGraph graph = attachMethodGraph(methodRef); DependencyNode[] varNodes = graph.getVariableNodes(); - schedulePropagation(varNodes[0], methodRef.getClassName()); + varNodes[0].propagate(methodRef.getClassName()); 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() { @Override public void run() { - targetNode.propagate(type); + consumer.consume(type); } }); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java index 1bfb152e7..d3c20b74f 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java @@ -5,7 +5,5 @@ package org.teavm.dependency; * @author Alexey Andreev */ public interface DependencyConsumer { - void propagate(String type); - - boolean hasType(String type); + void consume(String type); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index faa21d0af..21c4f93fa 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -30,7 +30,6 @@ class DependencyGraphBuilder { private DependencyNode resultNode; private Program program; private ValueType resultType; - private TypeAnalyzer typeAnalyzer; public DependencyGraphBuilder(DependencyChecker dependencyChecker) { this.dependencyChecker = dependencyChecker; @@ -86,7 +85,7 @@ class DependencyGraphBuilder { } @Override - public void propagate(String className) { + public void consume(String className) { if (DependencyChecker.shouldLog) { System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". Target class is " + className); @@ -100,22 +99,11 @@ class DependencyGraphBuilder { DependencyNode[] targetParams = targetGraph.getVariableNodes(); for (int i = 0; i < parameters.length; ++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) { 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) { diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java index b33976bfe..2d8040b3b 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -17,47 +17,76 @@ package org.teavm.dependency; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; /** * * @author Alexey Andreev */ -public class DependencyNode implements DependencyConsumer { +public class DependencyNode { private DependencyChecker dependencyChecker; private static final Object mapValue = new Object(); private ConcurrentMap followers = new ConcurrentHashMap<>(); private ConcurrentMap types = new ConcurrentHashMap<>(); + private ConcurrentMap transitions = new ConcurrentHashMap<>(); private volatile String tag; + private final AtomicReference arrayItemNode = new AtomicReference<>(); + private volatile CountDownLatch arrayItemNodeLatch = new CountDownLatch(1); DependencyNode(DependencyChecker dependencyChecker) { this.dependencyChecker = dependencyChecker; } - @Override public void propagate(String type) { if (types.putIfAbsent(type, mapValue) == null) { if (DependencyChecker.shouldLog) { System.out.println(tag + " -> " + type); } - for (DependencyConsumer follower : followers.keySet().toArray(new DependencyConsumer[0])) { - if (follower.hasType(type)) { - dependencyChecker.schedulePropagation(follower, type); - } + for (DependencyConsumer consumer : followers.keySet().toArray(new DependencyConsumer[0])) { + dependencyChecker.schedulePropagation(consumer, type); } } } - public void connect(DependencyConsumer follower) { - if (followers.putIfAbsent(follower, mapValue) == null) { + public void addConsumer(DependencyConsumer consumer) { + if (followers.putIfAbsent(consumer, mapValue) == null) { for (String type : types.keySet().toArray(new String[0])) { - if (follower.hasType(type)) { - dependencyChecker.schedulePropagation(follower, type); - } + dependencyChecker.schedulePropagation(consumer, 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) { return types.containsKey(type); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java new file mode 100644 index 000000000..0108e46a3 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java @@ -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()); + } + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/TypeAnalyzer.java b/teavm-core/src/main/java/org/teavm/dependency/TypeAnalyzer.java deleted file mode 100644 index c74398312..000000000 --- a/teavm-core/src/main/java/org/teavm/dependency/TypeAnalyzer.java +++ /dev/null @@ -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 - */ -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 path = getPathToRoot(firstClass); - return ValueType.object(findAmoungSupertypes(secondClass, path)); - } else { - return ValueType.object("java.lang.Object"); - } - } - - private Set getPathToRoot(ClassHolder cls) { - Set 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 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); - } -}