diff --git a/.idea/compiler.xml b/.idea/compiler.xml index dc58687b2..f5f5d00da 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -20,159 +20,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 1faa252d3..c752a1c4b 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -388,6 +388,19 @@ class DependencyGraphBuilder { DependencyNode node = nodes[receiver.getIndex()]; if (node != null) { node.propagate(dependencyChecker.getType("java.lang.Class")); + if (!(cst instanceof ValueType.Primitive)) { + StringBuilder sb = new StringBuilder(); + while (cst instanceof ValueType.Array) { + cst = ((ValueType.Array) cst).getItemType(); + sb.append('['); + } + if (cst instanceof ValueType.Object) { + sb.append(((ValueType.Object) cst).getClassName()); + } else { + sb.append(cst.toString()); + } + node.getClassValueNode().propagate(dependencyChecker.getType(sb.toString())); + } } while (cst instanceof ValueType.Array) { cst = ((ValueType.Array) cst).getItemType(); @@ -656,6 +669,10 @@ class DependencyGraphBuilder { invokeVirtual(receiver, instance, method, arguments); break; } + if (method.getName().equals("getClass") && method.parameterCount() == 0 + && method.getReturnType().isObject(Class.class) && receiver != null) { + nodes[instance.getIndex()].connect(nodes[receiver.getIndex()].getClassValueNode()); + } } } diff --git a/core/src/main/java/org/teavm/dependency/DependencyNode.java b/core/src/main/java/org/teavm/dependency/DependencyNode.java index ae2085f99..e3907fef5 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -30,6 +30,7 @@ public class DependencyNode implements ValueDependencyInfo { private List transitions; private volatile String tag; private DependencyNode arrayItemNode; + private DependencyNode classValueNode; private int degree; int index; boolean locked; @@ -201,6 +202,18 @@ public class DependencyNode implements ValueDependencyInfo { return arrayItemNode; } + public DependencyNode getClassValueNode() { + if (classValueNode == null) { + classValueNode = new DependencyNode(dependencyChecker, dependencyChecker.nodes.size(), degree); + dependencyChecker.nodes.add(classValueNode); + if (DependencyChecker.shouldLog) { + classValueNode.tag = tag + "@"; + } + classValueNode.addConsumer(this::propagate); + } + return classValueNode; + } + @Override public boolean hasArrayType() { return arrayItemNode != null && (arrayItemNode.types != null || arrayItemNode.smallTypes != null); diff --git a/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java b/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java index e20b478cf..ef4d1b789 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java +++ b/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java @@ -40,6 +40,9 @@ class DependencyNodeToNodeTransition implements DependencyConsumer { source.getArrayItem().connect(destination.getArrayItem()); destination.getArrayItem().connect(source.getArrayItem()); } + if (type.getName().equals("java.lang.Class")) { + source.getClassValueNode().connect(destination.getClassValueNode()); + } if (!destination.hasType(type)) { destination.propagate(type); } diff --git a/core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java b/core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java index f8c334535..df16f04f5 100644 --- a/core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java +++ b/core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java @@ -30,9 +30,9 @@ public interface MethodDependencyInfo { int getParameterCount(); - DependencyNode getResult(); + ValueDependencyInfo getResult(); - DependencyNode getThrown(); + ValueDependencyInfo getThrown(); MethodReference getReference(); diff --git a/core/src/main/java/org/teavm/dependency/ValueDependencyInfo.java b/core/src/main/java/org/teavm/dependency/ValueDependencyInfo.java index 81e878ee6..fb5731c9c 100644 --- a/core/src/main/java/org/teavm/dependency/ValueDependencyInfo.java +++ b/core/src/main/java/org/teavm/dependency/ValueDependencyInfo.java @@ -26,5 +26,7 @@ public interface ValueDependencyInfo { boolean hasArrayType(); - DependencyNode getArrayItem(); + ValueDependencyInfo getArrayItem(); + + ValueDependencyInfo getClassValueNode(); } diff --git a/tests/pom.xml b/tests/pom.xml index cdaa41378..5ae754df0 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -60,6 +60,12 @@ junit test + + org.teavm + teavm-tooling + ${project.version} + test + diff --git a/tests/src/test/java/org/teavm/dependency/ClassValueTest.java b/tests/src/test/java/org/teavm/dependency/ClassValueTest.java new file mode 100644 index 000000000..2dbb914ae --- /dev/null +++ b/tests/src/test/java/org/teavm/dependency/ClassValueTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.dependency; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Test; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.tooling.TeaVMProblemRenderer; +import org.teavm.tooling.TeaVMToolLog; +import org.teavm.vm.TeaVM; +import org.teavm.vm.TeaVMBuilder; + +public class ClassValueTest { + @Test + public void simple() { + ValueDependencyInfo info = runTestWithConsume("simpleSnippet").getClassValueNode(); + assertTrue("Long must be consumed", info.hasType("java.lang.Long")); + assertTrue("String must be consumed", info.hasType("java.lang.String")); + assertTrue("Nothing except Long and String expected", info.getTypes().length == 2); + } + + @SuppressWarnings("unused") + public static void simpleSnippet() { + consume(Long.class); + consume(String.class); + } + + @Test + public void fromGetClass() { + ValueDependencyInfo info = runTestWithConsume("fromGetClassSnippet").getClassValueNode(); + assertTrue("Long must be consumed", info.hasType("java.lang.Long")); + assertTrue("String must be consumed", info.hasType("java.lang.String")); + assertTrue("Nothing except Long and String expected", info.getTypes().length == 2); + } + + @SuppressWarnings("unused") + public static void fromGetClassSnippet() { + consumeClass(23L); + consumeClass("foo"); + } + + private static void consumeClass(Object value) { + consume(value.getClass()); + } + + protected static void consume(@SuppressWarnings("unused") Object value) { + // do nothing + } + + private ValueDependencyInfo runTestWithConsume(String methodName) { + DependencyInfo info = runTest(methodName); + MethodDependencyInfo methodInfo = info.getMethod(new MethodReference(ClassValueTest.class, "consume", + Object.class, void.class)); + if (methodInfo == null) { + fail("consume method never reached"); + } + return methodInfo.getVariable(1); + } + + private DependencyInfo runTest(String methodName) { + TeaVM vm = new TeaVMBuilder().build(); + vm.installPlugins(); + vm.entryPoint(new MethodReference(getClass().getName(), methodName, ValueType.VOID)); + vm.build(new StringBuilder(), null); + if (!vm.getProblemProvider().getSevereProblems().isEmpty()) { + fail("Code compiled with errors:\n" + describeProblems(vm)); + } + return vm.getDependencyInfo(); + } + + private String describeProblems(TeaVM vm) { + Log log = new Log(); + TeaVMProblemRenderer.describeProblems(vm, log); + return log.sb.toString(); + } + + + static class Log implements TeaVMToolLog { + StringBuilder sb = new StringBuilder(); + + @Override + public void info(String text) { + appendLine(text); + } + + @Override + public void debug(String text) { + appendLine(text); + } + + @Override + public void warning(String text) { + appendLine(text); + } + + @Override + public void error(String text) { + appendLine(text); + } + + @Override + public void info(String text, Throwable e) { + appendLine(text); + } + + @Override + public void debug(String text, Throwable e) { + appendLine(text); + } + + @Override + public void warning(String text, Throwable e) { + appendLine(text); + } + + @Override + public void error(String text, Throwable e) { + appendLine(text); + } + + private void appendLine(String text) { + sb.append(text).append('\n'); + } + } +} diff --git a/tests/teavm-tests.iml b/tests/teavm-tests.iml index 5e8336242..d0448738c 100644 --- a/tests/teavm-tests.iml +++ b/tests/teavm-tests.iml @@ -26,5 +26,6 @@ + \ No newline at end of file