diff --git a/core/src/main/java/org/teavm/javascript/Decompiler.java b/core/src/main/java/org/teavm/javascript/Decompiler.java index 3db516699..f79246ad8 100644 --- a/core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/core/src/main/java/org/teavm/javascript/Decompiler.java @@ -348,7 +348,7 @@ public class Decompiler { private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks, boolean async) { AsyncMethodPart result = new AsyncMethodPart(); lastBlockId = 1; - graph = ProgramUtils.buildControlFlowGraphWithTryCatch(program); + graph = ProgramUtils.buildControlFlowGraph(program); int[] weights = new int[graph.size()]; for (int i = 0; i < weights.length; ++i) { weights[i] = program.basicBlockAt(i).getInstructions().size(); @@ -391,7 +391,7 @@ public class Decompiler { generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null; } - closeExpiredBookmarks(generator, generator.currentBlock.getTryCatchBlocks()); + closeExpiredBookmarks(generator, node, generator.currentBlock.getTryCatchBlocks()); List inheritedBookmarks = new ArrayList<>(); Block block = stack.peek(); @@ -475,7 +475,7 @@ public class Decompiler { return result; } - private void closeExpiredBookmarks(StatementGenerator generator, List tryCatchBlocks) { + private void closeExpiredBookmarks(StatementGenerator generator, int node, List tryCatchBlocks) { tryCatchBlocks = new ArrayList<>(tryCatchBlocks); Collections.reverse(tryCatchBlocks); @@ -514,8 +514,10 @@ public class Decompiler { TryCatchStatement tryCatchStmt = new TryCatchStatement(); tryCatchStmt.setExceptionType(bookmark.exceptionType); tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable); - tryCatchStmt.getHandler().add(generator.generateJumpStatement( - program.basicBlockAt(bookmark.exceptionHandler))); + if (node != bookmark.exceptionHandler) { + tryCatchStmt.getHandler().add(generator.generateJumpStatement( + program.basicBlockAt(bookmark.exceptionHandler))); + } List blockPart = block.body.subList(bookmark.offset, block.body.size()); tryCatchStmt.getProtectedBody().addAll(blockPart); blockPart.clear(); diff --git a/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java b/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java index e49c9c2fc..009b961fe 100644 --- a/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java +++ b/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java @@ -151,7 +151,7 @@ public class AsyncProgramSplitter { IntegerArray splitPoints = IntegerArray.of(part.splitPoints); AsyncProgramSplittingBackend splittingBackend = new AsyncProgramSplittingBackend( new ProgramNodeSplittingBackend(part.program), blockSuccessors, originalBlocks, splitPoints); - Graph graph = ProgramUtils.buildControlFlowGraphWithTryCatch(part.program); + Graph graph = ProgramUtils.buildControlFlowGraph(part.program); int[] weights = new int[graph.size()]; for (int i = 0; i < part.program.basicBlockCount(); ++i) { weights[i] = part.program.basicBlockAt(i).getInstructions().size(); diff --git a/core/src/main/java/org/teavm/testing/JUnitTestAdapter.java b/core/src/main/java/org/teavm/testing/JUnitTestAdapter.java index 465ba54d3..c5ef706d9 100644 --- a/core/src/main/java/org/teavm/testing/JUnitTestAdapter.java +++ b/core/src/main/java/org/teavm/testing/JUnitTestAdapter.java @@ -56,4 +56,9 @@ public class JUnitTestAdapter implements TestAdapter { } return Collections.emptyList(); } + + @Override + public Class getRunner(MethodReader method) { + return SimpleTestRunner.class; + } } diff --git a/core/src/main/java/org/teavm/testing/SimpleTestRunner.java b/core/src/main/java/org/teavm/testing/SimpleTestRunner.java new file mode 100644 index 000000000..e5ecdb911 --- /dev/null +++ b/core/src/main/java/org/teavm/testing/SimpleTestRunner.java @@ -0,0 +1,27 @@ +/* + * Copyright 2015 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.testing; + +/** + * + * @author Alexey Andreev + */ +public class SimpleTestRunner implements TestRunner { + @Override + public void run(TestLauncher launcher) throws Throwable { + launcher.launch(); + } +} diff --git a/core/src/main/java/org/teavm/testing/TestAdapter.java b/core/src/main/java/org/teavm/testing/TestAdapter.java index 703339a75..da3cf457c 100644 --- a/core/src/main/java/org/teavm/testing/TestAdapter.java +++ b/core/src/main/java/org/teavm/testing/TestAdapter.java @@ -27,4 +27,6 @@ public interface TestAdapter { boolean acceptMethod(MethodReader method); Iterable getExpectedExceptions(MethodReader method); + + Class getRunner(MethodReader method); } diff --git a/core/src/main/java/org/teavm/testing/TestLauncher.java b/core/src/main/java/org/teavm/testing/TestLauncher.java new file mode 100644 index 000000000..d60d55948 --- /dev/null +++ b/core/src/main/java/org/teavm/testing/TestLauncher.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 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.testing; + +/** + * + * @author Alexey Andreev + */ +public interface TestLauncher { + void launch() throws Throwable; +} diff --git a/core/src/main/java/org/teavm/testing/TestRunner.java b/core/src/main/java/org/teavm/testing/TestRunner.java new file mode 100644 index 000000000..389a72944 --- /dev/null +++ b/core/src/main/java/org/teavm/testing/TestRunner.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 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.testing; + +/** + * + * @author Alexey Andreev + */ +public interface TestRunner { + void run(TestLauncher launcher) throws Throwable; +} diff --git a/html4j/src/main/java/org/teavm/html4j/testing/KOTestAdapter.java b/html4j/src/main/java/org/teavm/html4j/testing/KOTestAdapter.java index 4897ac3b3..7ac058f1d 100644 --- a/html4j/src/main/java/org/teavm/html4j/testing/KOTestAdapter.java +++ b/html4j/src/main/java/org/teavm/html4j/testing/KOTestAdapter.java @@ -21,6 +21,7 @@ import java.util.Collections; import org.netbeans.html.json.tck.KOTest; import org.teavm.model.MethodReader; import org.teavm.testing.TestAdapter; +import org.teavm.testing.TestRunner; /** * @@ -48,4 +49,9 @@ public class KOTestAdapter implements TestAdapter { public Iterable getExpectedExceptions(MethodReader method) { return Collections.emptyList(); } + + @Override + public Class getRunner(MethodReader method) { + return KOTestRunner.class; + } } diff --git a/html4j/src/main/java/org/teavm/html4j/testing/KOTestRunner.java b/html4j/src/main/java/org/teavm/html4j/testing/KOTestRunner.java new file mode 100644 index 000000000..2892bd811 --- /dev/null +++ b/html4j/src/main/java/org/teavm/html4j/testing/KOTestRunner.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 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.html4j.testing; + +import org.teavm.testing.TestLauncher; +import org.teavm.testing.TestRunner; + +/** + * + * @author Alexey Andreev + */ +public class KOTestRunner implements TestRunner { + @Override + public void run(TestLauncher launcher) throws Throwable { + int repeatCount = 0; + while (true) { + try { + launcher.launch(); + break; + } catch (InterruptedException e) { + if (++repeatCount == 10) { + throw e; + } + } catch (Throwable e) { + throw e; + } + } + } +} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java b/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java index 07ed45c81..484397246 100644 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java @@ -44,7 +44,6 @@ import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; import org.teavm.model.PreOptimizingClassHolderSource; import org.teavm.model.ProgramCache; -import org.teavm.model.ValueType; import org.teavm.parsing.ClasspathClassHolderSource; import org.teavm.testing.JUnitTestAdapter; import org.teavm.testing.TestAdapter; @@ -362,12 +361,14 @@ public class TeaVMTestTool implements BaseTeaVMTool { exceptions.add(exception); } - TestMethodBuilder testMethod = new TestMethodBuilder(ref, fileName, exceptions); + String runner = adapter.getRunner(method).getName(); + + TestMethodBuilder testMethod = new TestMethodBuilder(ref, fileName, exceptions, runner); testClass.getMethods().add(testMethod); String debugTable = debugInformationGenerated ? testMethod.getFileName() + ".teavmdbg" : null; cases.add(new TestCase(ref.toString(), testMethod.getFileName(), debugTable, - testMethod.getExpectedExceptions())); + testMethod.getExpectedExceptions(), runner)); ++testCount; } } @@ -417,6 +418,7 @@ public class TeaVMTestTool implements BaseTeaVMTool { vm.setMinifying(minifying); vm.installPlugins(); new TestExceptionPlugin().install(vm); + new TestEntryPointTransformer(testMethod.getRunner(), testMethod.getMethod()).install(vm); for (ClassHolderTransformer transformer : transformers) { vm.add(transformer); } @@ -426,13 +428,10 @@ public class TeaVMTestTool implements BaseTeaVMTool { ? new DebugInformationBuilder() : null; MethodReference methodRef = testMethod.getMethod(); try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { - MethodReference cons = new MethodReference(methodRef.getClassName(), "", ValueType.VOID); MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException", Throwable.class, String.class); - vm.entryPoint("initInstance", cons); - vm.entryPoint("runTest", methodRef).withValue(0, cons.getClassName()).async(); + vm.entryPoint("runTest", new MethodReference(TestEntryPoint.class, "run", void.class)).async(); vm.entryPoint("extractException", exceptionMsg); - vm.exportType("TestClass", cons.getClassName()); vm.setDebugEmitter(debugInfoBuilder); vm.build(innerWriter, new DirectoryBuildTarget(outputDir)); innerWriter.append("\n"); diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java index 5c0ae9e1b..825b46752 100644 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java @@ -31,17 +31,20 @@ public class TestCase { private String testScript; private String debugTable; private List expectedExceptions = new ArrayList<>(); + private String runner; @JsonCreator public TestCase( @JsonProperty("testMethod") String testMethod, @JsonProperty("script") String testScript, @JsonProperty("debugTable") String debugTable, - @JsonProperty("expectedExceptions") List expectedExceptions) { + @JsonProperty("expectedExceptions") List expectedExceptions, + @JsonProperty("runner") String runner) { this.testMethod = testMethod; this.testScript = testScript; this.debugTable = debugTable; this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); + this.runner = runner; } @JsonGetter @@ -63,4 +66,9 @@ public class TestCase { public List getExpectedExceptions() { return expectedExceptions; } + + @JsonGetter + public String getRunner() { + return runner; + } } diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPoint.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPoint.java new file mode 100644 index 000000000..e67e8974b --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPoint.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015 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.tooling.testing; + +import org.teavm.testing.TestRunner; + +/** + * + * @author Alexey Andreev + */ +final class TestEntryPoint { + private TestEntryPoint() { + } + + public static void run() throws Throwable { + createRunner().run(() -> launchTest()); + } + + private static native TestRunner createRunner(); + + private static native void launchTest(); +} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPointTransformer.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPointTransformer.java new file mode 100644 index 000000000..dd631020f --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPointTransformer.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 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.tooling.testing; + +import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.ElementModifier; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.emit.ProgramEmitter; +import org.teavm.vm.spi.TeaVMHost; +import org.teavm.vm.spi.TeaVMPlugin; + +/** + * + * @author Alexey Andreev + */ +class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin { + private String runnerClassName; + private MethodReference testMethod; + + public TestEntryPointTransformer(String runnerClassName, MethodReference testMethod) { + this.runnerClassName = runnerClassName; + this.testMethod = testMethod; + } + + @Override + public void install(TeaVMHost host) { + host.add(this); + } + + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { + if (cls.getName().equals(TestEntryPoint.class.getName())) { + for (MethodHolder method : cls.getMethods()) { + if (method.equals(method)) { + if (method.getName().equals("createRunner")) { + method.setProgram(generateRunnerProgram(method, innerSource)); + method.getModifiers().remove(ElementModifier.NATIVE); + } else if (method.getName().equals("launchTest")) { + method.setProgram(generateLaunchProgram(method, innerSource)); + method.getModifiers().remove(ElementModifier.NATIVE); + } + } + } + } + } + + private Program generateRunnerProgram(MethodHolder method, ClassReaderSource innerSource) { + ProgramEmitter pe = ProgramEmitter.create(method, innerSource); + pe.construct(runnerClassName).returnValue(); + return pe.getProgram(); + } + + private Program generateLaunchProgram(MethodHolder method, ClassReaderSource innerSource) { + ProgramEmitter pe = ProgramEmitter.create(method, innerSource); + pe.construct(testMethod.getClassName()).invokeSpecial(testMethod); + pe.exit(); + return pe.getProgram(); + } +} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java index 3faa384d7..9f81a41f6 100644 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java @@ -28,11 +28,14 @@ class TestMethodBuilder { private MethodReference method; private String fileName; private List expectedExceptions = new ArrayList<>(); + private String runner; - public TestMethodBuilder(MethodReference method, String fileName, List expectedExceptions) { + public TestMethodBuilder(MethodReference method, String fileName, List expectedExceptions, + String runner) { this.method = method; this.fileName = fileName; this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); + this.runner = runner; } public MethodReference getMethod() { @@ -46,4 +49,8 @@ class TestMethodBuilder { public List getExpectedExceptions() { return expectedExceptions; } + + public String getRunner() { + return runner; + } } diff --git a/tools/core/src/main/resources/org/teavm/tooling/test/res/junit-client.js b/tools/core/src/main/resources/org/teavm/tooling/test/res/junit-client.js index 638402aba..12a309bd3 100644 --- a/tools/core/src/main/resources/org/teavm/tooling/test/res/junit-client.js +++ b/tools/core/src/main/resources/org/teavm/tooling/test/res/junit-client.js @@ -25,25 +25,8 @@ JUnitClient.runTest = function() { } loop: while (true) { switch (ptr) { case 0: - instance = new TestClass(); - ptr = 1; - case 1: try { - initInstance(instance); - } catch (e) { - message = {}; - JUnitClient.makeErrorMessage(message, e); - break loop; - } - if (thread.isSuspending()) { - thread.push(instance); - thread.push(ptr); - return; - } - ptr = 2; - case 2: - try { - runTest(instance); + runTest(); } catch (e) { message = {}; JUnitClient.makeErrorMessage(message, e); diff --git a/tools/eclipse/core-plugin/META-INF/MANIFEST.MF b/tools/eclipse/core-plugin/META-INF/MANIFEST.MF index b4bee0aa9..1165cbd17 100644 --- a/tools/eclipse/core-plugin/META-INF/MANIFEST.MF +++ b/tools/eclipse/core-plugin/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 0.4.0.qualifier Bundle-Vendor: Alexey Andreev Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ClassPath: ., - lib/asm-debug-all-5.0.3.jar, + lib/asm-debug-all-5.0.4.jar, lib/cdi-api-1.2.jar, lib/commons-io-2.4.jar, lib/jackson-core-asl-1.9.13.jar, diff --git a/tools/eclipse/core-plugin/build.properties b/tools/eclipse/core-plugin/build.properties index 95269dd03..a2a337acd 100644 --- a/tools/eclipse/core-plugin/build.properties +++ b/tools/eclipse/core-plugin/build.properties @@ -3,7 +3,7 @@ output.. = target/ bin.includes = META-INF/,\ .,\ lib/,\ - lib/asm-debug-all-5.0.3.jar,\ + lib/asm-debug-all-5.0.4.jar,\ lib/cdi-api-1.2.jar,\ lib/commons-io-2.4.jar,\ lib/jackson-core-asl-1.9.13.jar,\ diff --git a/tools/maven/plugin/src/main/resources/teavm-htmlunit-adapter.js b/tools/maven/plugin/src/main/resources/teavm-htmlunit-adapter.js index 93d549474..2e8cce268 100644 --- a/tools/maven/plugin/src/main/resources/teavm-htmlunit-adapter.js +++ b/tools/maven/plugin/src/main/resources/teavm-htmlunit-adapter.js @@ -12,25 +12,8 @@ function(callback) { } loop: while (true) { switch (ptr) { case 0: - instance = new TestClass(); - ptr = 1; - case 1: try { - initInstance(instance); - } catch (e) { - message = {}; - JUnitClient.makeErrorMessage(message, e); - break loop; - } - if (thread.isSuspending()) { - thread.push(instance); - thread.push(ptr); - return; - } - ptr = 2; - case 2: - try { - runTest(instance); + runTest(); } catch (e) { message = {}; JUnitClient.makeErrorMessage(message, e); diff --git a/tools/maven/plugin/src/main/resources/teavm-selenium-adapter.js b/tools/maven/plugin/src/main/resources/teavm-selenium-adapter.js index 91cf0cf83..49990e3b0 100644 --- a/tools/maven/plugin/src/main/resources/teavm-selenium-adapter.js +++ b/tools/maven/plugin/src/main/resources/teavm-selenium-adapter.js @@ -11,25 +11,8 @@ JUnitClient.run = function() { } loop: while (true) { switch (ptr) { case 0: - instance = new TestClass(); - ptr = 1; - case 1: try { - initInstance(instance); - } catch (e) { - message = {}; - JUnitClient.makeErrorMessage(message, e); - break loop; - } - if (thread.isSuspending()) { - thread.push(instance); - thread.push(ptr); - return; - } - ptr = 2; - case 2: - try { - runTest(instance); + runTest(); } catch (e) { message = {}; JUnitClient.makeErrorMessage(message, e);