From 73721e5b3159abc87149be50d34cf7f5999d2f6d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 5 Feb 2015 17:50:25 +0400 Subject: [PATCH 1/6] Add exception support to async methods --- .../java/lang/ThreadNativeGenerator.java | 4 +- .../src/main/java/org/teavm/cache/AstIO.java | 16 + .../cache/DiskRegularMethodNodeCache.java | 4 + .../javascript/BreakToContinueReplacer.java | 4 + .../javascript/CertainBlockCountVisitor.java | 4 + .../java/org/teavm/javascript/Decompiler.java | 60 +- .../teavm/javascript/OptimizingVisitor.java | 5 + .../javascript/RedundantLabelEliminator.java | 4 + .../javascript/ReferenceCountingVisitor.java | 4 + .../java/org/teavm/javascript/Renderer.java | 50 +- .../org/teavm/javascript/TryCatchFinder.java | 4 + .../javascript/UnusedVariableEliminator.java | 4 + .../teavm/javascript/ast/AsyncMethodPart.java | 17 - .../teavm/javascript/ast/RenamingVisitor.java | 7 + ...dCatch.java => RestoreAsyncStatement.java} | 30 +- .../javascript/ast/StatementVisitor.java | 2 + .../teavm/javascript/ni/GeneratorContext.java | 2 - .../model/util/AsyncProgramSplitter.java | 6 +- .../java/org/teavm/parsing/ProgramParser.java | 3 + .../resources/org/teavm/javascript/runtime.js | 30 +- .../org/teavm/tooling/test/res/runtime.js | 732 ------------------ .../org/teavm/samples/async/AsyncProgram.java | 13 + .../src/main/webapp/index.html | 2 +- 23 files changed, 174 insertions(+), 833 deletions(-) rename teavm-core/src/main/java/org/teavm/javascript/ast/{AsyncMethodCatch.java => RestoreAsyncStatement.java} (51%) delete mode 100644 teavm-core/src/main/resources/org/teavm/tooling/test/res/runtime.js diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java index 2560d7931..3c2c8b5bd 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java @@ -37,13 +37,13 @@ public class ThreadNativeGenerator implements Generator { private void generateSleep(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("setTimeout(function() {").indent().softNewLine(); - writer.append(context.getCompleteContinuation()).append("();").softNewLine(); + writer.append(context.getCompleteContinuation()).append("($rt_asyncResult(null));").softNewLine(); writer.outdent().append("},").ws().append(context.getParameterName(1)).append(");").softNewLine(); } private void generateYield(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("setTimeout(function() {").indent().softNewLine(); - writer.append(context.getCompleteContinuation()).append("();").softNewLine(); + writer.append(context.getCompleteContinuation()).append("($rt_asyncResult(null));").softNewLine(); writer.outdent().append("},").ws().append("0);").softNewLine(); } } diff --git a/teavm-core/src/main/java/org/teavm/cache/AstIO.java b/teavm-core/src/main/java/org/teavm/cache/AstIO.java index b750fddb4..3fda51324 100644 --- a/teavm-core/src/main/java/org/teavm/cache/AstIO.java +++ b/teavm-core/src/main/java/org/teavm/cache/AstIO.java @@ -306,6 +306,16 @@ public class AstIO { } } + @Override + public void visit(RestoreAsyncStatement statement) { + try { + output.writeByte(17); + output.writeShort(statement.getReceiver() != null ? statement.getReceiver() : -1); + } catch (IOException e) { + throw new IOExceptionWrapper(e); + } + } + @Override public void visit(BinaryExpr expr) { try { @@ -651,6 +661,12 @@ public class AstIO { readSequence(input, stmt.getHandler()); return stmt; } + case 17: { + short var = input.readShort(); + RestoreAsyncStatement stmt = new RestoreAsyncStatement(); + stmt.setReceiver(var >= 0 ? (int)var : null); + return stmt; + } default: throw new RuntimeException("Unexpected statement type: " + type); } diff --git a/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java b/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java index 718ef40ca..33253cd80 100644 --- a/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java +++ b/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java @@ -257,6 +257,10 @@ public class DiskRegularMethodNodeCache implements RegularMethodNodeCache { @Override public void visit(StaticClassExpr expr) { } + + @Override + public void visit(RestoreAsyncStatement statement) { + } } static class Item { diff --git a/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java b/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java index 63bbfab24..de500d61f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java @@ -112,4 +112,8 @@ class BreakToContinueReplacer implements StatementVisitor { visitSequence(statement.getProtectedBody()); visitSequence(statement.getHandler()); } + + @Override + public void visit(RestoreAsyncStatement statement) { + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/CertainBlockCountVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/CertainBlockCountVisitor.java index 7274337c9..bd475a54a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/CertainBlockCountVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/CertainBlockCountVisitor.java @@ -107,4 +107,8 @@ class CertainBlockCountVisitor implements StatementVisitor { visit(statement.getProtectedBody()); visit(statement.getHandler()); } + + @Override + public void visit(RestoreAsyncStatement statement) { + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index b700f9e53..affd53dd4 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -204,8 +204,15 @@ public class Decompiler { splitter.split(method.getProgram()); List partPrograms = new ArrayList<>(); for (int i = 0; i < splitter.size(); ++i) { - AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i)); - part.setInputVariable(splitter.getInput(i)); + Integer input = null; + if (i > 0) { + input = splitter.getInput(i); + if (input == null) { + input = -1; + } + } + AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i), + input); node.getBody().add(part); partPrograms.add(splitter.getProgram(i)); } @@ -229,7 +236,7 @@ public class Decompiler { Program program = method.getProgram(); int[] targetBlocks = new int[program.basicBlockCount()]; Arrays.fill(targetBlocks, -1); - methodNode.setBody(getRegularMethodStatement(program, targetBlocks).getStatement()); + methodNode.setBody(getRegularMethodStatement(program, targetBlocks, null).getStatement()); for (int i = 0; i < program.variableCount(); ++i) { methodNode.getVariables().add(program.variableAt(i).getRegister()); } @@ -244,7 +251,7 @@ public class Decompiler { return methodNode; } - private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks) { + private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks, Integer inputVar) { AsyncMethodPart result = new AsyncMethodPart(); lastBlockId = 1; graph = ProgramUtils.buildControlFlowGraph(program); @@ -297,11 +304,15 @@ public class Decompiler { int tmp = indexer.nodeAt(next); generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null; generator.statements.clear(); + if (node == 0 && inputVar != null) { + RestoreAsyncStatement restoreStmt = new RestoreAsyncStatement(); + restoreStmt.setReceiver(inputVar >= 0 ? inputVar : null); + generator.statements.add(restoreStmt); + } generator.asyncTarget = null; InstructionLocation lastLocation = null; NodeLocation nodeLocation = null; List instructions = generator.currentBlock.getInstructions(); - boolean asyncInvocation = false; for (int j = 0; j < instructions.size(); ++j) { Instruction insn = generator.currentBlock.getInstructions().get(j); if (insn.getLocation() != null && lastLocation != insn.getLocation()) { @@ -313,41 +324,20 @@ public class Decompiler { } if (targetBlocks[node] >= 0 && j == instructions.size() - 1) { generator.asyncTarget = targetBlocks[node]; - asyncInvocation = true; } insn.acceptVisitor(generator); } - boolean hasAsyncCatch = false; for (TryCatchBlock tryCatch : generator.currentBlock.getTryCatchBlocks()) { - if (asyncInvocation) { - TryCatchStatement tryCatchStmt = new TryCatchStatement(); - tryCatchStmt.setExceptionType(tryCatch.getExceptionType()); - tryCatchStmt.setExceptionVariable(tryCatch.getExceptionVariable().getIndex()); - tryCatchStmt.getProtectedBody().addAll(generator.statements); - generator.statements.clear(); - generator.statements.add(tryCatchStmt); - Statement handlerStmt = generator.generateJumpStatement(tryCatch.getHandler()); - if (handlerStmt != null) { - tryCatchStmt.getHandler().add(handlerStmt); - } - } else { - AsyncMethodCatch asyncCatch = new AsyncMethodCatch(); - asyncCatch.setExceptionType(tryCatch.getExceptionType()); - asyncCatch.setExceptionVariable(tryCatch.getExceptionVariable().getIndex()); - Statement handlerStmt = generator.generateJumpStatement(tryCatch.getHandler()); - if (handlerStmt != null) { - asyncCatch.getHandler().add(handlerStmt); - } - result.getCatches().add(asyncCatch); - hasAsyncCatch = true; - } - } - if (hasAsyncCatch) { - TryCatchStatement guardTryCatch = new TryCatchStatement(); - guardTryCatch.setAsync(true); - guardTryCatch.getProtectedBody().addAll(generator.statements); + TryCatchStatement tryCatchStmt = new TryCatchStatement(); + tryCatchStmt.setExceptionType(tryCatch.getExceptionType()); + tryCatchStmt.setExceptionVariable(tryCatch.getExceptionVariable().getIndex()); + tryCatchStmt.getProtectedBody().addAll(generator.statements); generator.statements.clear(); - generator.statements.add(guardTryCatch); + generator.statements.add(tryCatchStmt); + Statement handlerStmt = generator.generateJumpStatement(tryCatch.getHandler()); + if (handlerStmt != null) { + tryCatchStmt.getHandler().add(handlerStmt); + } } block.body.addAll(generator.statements); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java index 2d8dd3528..2d57ee859 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java @@ -577,4 +577,9 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { statement.getHandler().addAll(statements); resultStmt = statement; } + + @Override + public void visit(RestoreAsyncStatement statement) { + resultStmt = statement; + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java index 92af7a2b8..30111b45d 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java @@ -117,4 +117,8 @@ class RedundantLabelEliminator implements StatementVisitor { visitSequence(statement.getProtectedBody()); visitSequence(statement.getHandler()); } + + @Override + public void visit(RestoreAsyncStatement statement) { + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java index f08dfcbb0..1e5f742af 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java @@ -111,4 +111,8 @@ class ReferenceCountingVisitor implements StatementVisitor { part.acceptVisitor(this); } } + + @Override + public void visit(RestoreAsyncStatement statement) { + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index e2ae3b11b..545a26032 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -529,7 +529,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext if (startParam < ref.parameterCount() + 1) { writer.append(',').ws(); } - writer.append("$return,").ws().append("$throw"); + writer.append("$return"); } writer.append(")").ws().append("{").softNewLine().indent(); method.acceptVisitor(new MethodBodyRenderer()); @@ -609,6 +609,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { variableNames.add(variableName(i)); } + TryCatchFinder tryCatchFinder = new TryCatchFinder(); + for (AsyncMethodPart part : methodNode.getBody()) { + if (!tryCatchFinder.tryCatchFound) { + part.getStatement().acceptVisitor(tryCatchFinder); + } + } + boolean hasTryCatch = tryCatchFinder.tryCatchFound; + if (hasTryCatch) { + variableNames.add("$je"); + } if (!variableNames.isEmpty()) { writer.append("var "); for (int i = 0; i < variableNames.size(); ++i) { @@ -620,15 +630,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append(";").softNewLine(); } for (int i = 0; i < methodNode.getBody().size(); ++i) { - writer.append("function $part_").append(i).append("($input)").ws().append('{') - .indent().softNewLine(); - AsyncMethodPart part = methodNode.getBody().get(i); - if (part.getInputVariable() != null) { - writer.append(variableName(part.getInputVariable())).ws().append('=').ws().append("$input;") - .softNewLine(); + writer.append("function $part_").append(i).append("("); + if (i > 0) { + writer.append("$restore"); } + writer.append(")").ws().append('{').indent().softNewLine(); + writer.append("try {").indent().softNewLine(); + AsyncMethodPart part = methodNode.getBody().get(i); part.getStatement().acceptVisitor(Renderer.this); - writer.outdent().append('}').softNewLine(); + writer.outdent().append("} catch ($guard) {").indent().softNewLine(); + writer.append("return $return($rt_asyncError($guard));").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.outdent().append("}").softNewLine(); } writer.append("return $part_0();").softNewLine(); } catch (IOException e) { @@ -666,11 +679,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext return async; } - @Override - public String getErrorContinuation() { - return "$throw"; - } - @Override public String getCompleteContinuation() { return "$return"; @@ -912,7 +920,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } writer.append("return"); if (async) { - writer.append(" $return("); + writer.append(" $return($rt_asyncResult("); } if (statement.getResult() != null) { writer.append(' '); @@ -921,7 +929,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext debugEmitter.emitCallSite(); } if (async) { - writer.append(')'); + writer.append("))"); } writer.append(";").softNewLine(); if (statement.getLocation() != null) { @@ -1754,6 +1762,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } } + @Override + public void visit(RestoreAsyncStatement statement) { + try { + if (statement.getReceiver() != null) { + writer.append(variableName(statement.getReceiver())).ws().append('=').ws(); + } + writer.append("$restore();").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } + } + private Injector getInjector(MethodReference ref) { InjectorHolder holder = injectorMap.get(ref); if (holder == null) { diff --git a/teavm-core/src/main/java/org/teavm/javascript/TryCatchFinder.java b/teavm-core/src/main/java/org/teavm/javascript/TryCatchFinder.java index 1c53f647f..7618fb355 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/TryCatchFinder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/TryCatchFinder.java @@ -110,4 +110,8 @@ class TryCatchFinder implements StatementVisitor { public void visit(TryCatchStatement statement) { tryCatchFound = true; } + + @Override + public void visit(RestoreAsyncStatement statement) { + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java index 9fecea076..719a1a842 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java @@ -224,4 +224,8 @@ class UnusedVariableEliminator implements ExprVisitor, StatementVisitor { } } } + + @Override + public void visit(RestoreAsyncStatement statement) { + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodPart.java b/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodPart.java index 85f216951..d55e653b8 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodPart.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodPart.java @@ -15,17 +15,12 @@ */ package org.teavm.javascript.ast; -import java.util.ArrayList; -import java.util.List; - /** * * @author Alexey Andreev */ public class AsyncMethodPart { private Statement statement; - private Integer inputVariable; - private List catches = new ArrayList<>(); public Statement getStatement() { return statement; @@ -34,16 +29,4 @@ public class AsyncMethodPart { public void setStatement(Statement statement) { this.statement = statement; } - - public Integer getInputVariable() { - return inputVariable; - } - - public void setInputVariable(Integer inputVariable) { - this.inputVariable = inputVariable; - } - - public List getCatches() { - return catches; - } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/RenamingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/ast/RenamingVisitor.java index bb0e1afd4..cb3b363ec 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/RenamingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/RenamingVisitor.java @@ -191,4 +191,11 @@ public class RenamingVisitor implements StatementVisitor, ExprVisitor { } statement.setExceptionVariable(varNames[statement.getExceptionVariable()]); } + + @Override + public void visit(RestoreAsyncStatement statement) { + if (statement.getReceiver() != null) { + statement.setReceiver(varNames[statement.getReceiver()]); + } + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodCatch.java b/teavm-core/src/main/java/org/teavm/javascript/ast/RestoreAsyncStatement.java similarity index 51% rename from teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodCatch.java rename to teavm-core/src/main/java/org/teavm/javascript/ast/RestoreAsyncStatement.java index 09d79fa25..4f903fd9b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodCatch.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/RestoreAsyncStatement.java @@ -15,35 +15,23 @@ */ package org.teavm.javascript.ast; -import java.util.ArrayList; -import java.util.List; - /** * * @author Alexey Andreev */ -public class AsyncMethodCatch { - private List handler = new ArrayList<>(); - private String exceptionType; - private Integer exceptionVariable; +public class RestoreAsyncStatement extends Statement { + private Integer receiver; - public List getHandler() { - return handler; + public Integer getReceiver() { + return receiver; } - public String getExceptionType() { - return exceptionType; + public void setReceiver(Integer receiver) { + this.receiver = receiver; } - public void setExceptionType(String exceptionType) { - this.exceptionType = exceptionType; - } - - public Integer getExceptionVariable() { - return exceptionVariable; - } - - public void setExceptionVariable(Integer exceptionVariable) { - this.exceptionVariable = exceptionVariable; + @Override + public void acceptVisitor(StatementVisitor visitor) { + visitor.visit(this); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/StatementVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/ast/StatementVisitor.java index 62fcc8fbb..8d984747f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/StatementVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/StatementVisitor.java @@ -43,4 +43,6 @@ public interface StatementVisitor { void visit(InitClassStatement statement); void visit(TryCatchStatement statement); + + void visit(RestoreAsyncStatement statement); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java index c28756a55..047d67c0c 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java @@ -34,7 +34,5 @@ public interface GeneratorContext extends ServiceRepository { boolean isAsync(); - String getErrorContinuation(); - String getCompleteContinuation(); } diff --git a/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java b/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java index 602a58c15..9734e2c19 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java +++ b/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java @@ -68,7 +68,8 @@ public class AsyncProgramSplitter { // Copy portion of current block from last occurence (or from start) to i'th instruction. targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, last, i + 1, targetBlock.getProgram())); - ProgramUtils.copyTryCatches(sourceBlock, targetBlock.getProgram()); + targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, + targetBlock.getProgram())); for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) { if (tryCatch.getHandler() != null) { Step next = new Step(); @@ -107,12 +108,15 @@ public class AsyncProgramSplitter { JumpInstruction jumpToNextBlock = new JumpInstruction(); jumpToNextBlock.setTarget(targetBlock); nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock); + nextProgram.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, + nextProgram)); } step.targetPart = part; } } targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, last, sourceBlock.getInstructions().size(), targetBlock.getProgram())); + targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, targetBlock.getProgram())); for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) { if (tryCatch.getHandler() != null) { Step next = new Step(); diff --git a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java index dd87b0c4b..f3ab0237b 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -21,6 +21,7 @@ import org.objectweb.asm.tree.*; import org.teavm.model.*; import org.teavm.model.instructions.*; import org.teavm.model.util.InstructionTransitionExtractor; +import org.teavm.model.util.ProgramUtils; /** * @@ -112,6 +113,8 @@ public class ProgramParser implements VariableDebugInformation { while (program.variableCount() <= signatureVars) { program.createVariable(); } + program.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches( + program.basicBlockAt(1), program)); return program; } diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index e71694365..0fd948135 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -392,25 +392,41 @@ function $rt_virtualMethods(cls) { } } } +function $rt_asyncResult(value) { + return function() { + return value; + } +} +function $rt_asyncError(e) { + return function() { + throw e; + } +} function $rt_asyncAdapter(f) { return function() { var e; + var result; var args = Array.prototype.slice.apply(arguments); - var $throw = args.pop(); var $return = args.pop(); try { - var result = f.apply(this, args); + result = f.apply(this, args); } catch (e) { - return $throw(e); + return $rt_asyncError(e); } - return $return(result); + return $rt_asyncResult(result); } } -function $rt_rootInvocationAdapter(f) { +function $rt_rootInvocationAdapter(f, extraArgs) { return function() { var args = Array.prototype.slice.apply(arguments); - args.push(function() {}); - args.push(function() {}); + if (extraArgs) { + for (var i = 0; i < extraArts.length; ++i) { + args.push(extraArgs[i]); + } + } + args.push(function(result) { + result(); + }); return f.apply(this, args); } } diff --git a/teavm-core/src/main/resources/org/teavm/tooling/test/res/runtime.js b/teavm-core/src/main/resources/org/teavm/tooling/test/res/runtime.js deleted file mode 100644 index 4ab5dea35..000000000 --- a/teavm-core/src/main/resources/org/teavm/tooling/test/res/runtime.js +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Copyright 2013 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. - */ -$rt_lastObjectId = 0; -$rt_nextId = function() { - return $rt_lastObjectId++; -} -$rt_compare = function(a, b) { - return a > b ? 1 : a < b ? -1 : 0; -} -$rt_isInstance = function(obj, cls) { - return obj != null && obj.constructor.$meta && $rt_isAssignable(obj.constructor, cls); -} -$rt_isAssignable = function(from, to) { - if (from === to) { - return true; - } - var supertypes = from.$meta.supertypes; - for (var i = 0; i < supertypes.length; i = (i + 1) | 0) { - if ($rt_isAssignable(supertypes[i], to)) { - return true; - } - } - return false; -} -$rt_createArray = function(cls, sz) { - var data = new Array(sz); - var arr = new ($rt_arraycls(cls))(data); - for (var i = 0; i < sz; i = (i + 1) | 0) { - data[i] = null; - } - return arr; -} -$rt_wrapArray = function(cls, data) { - var arr = new ($rt_arraycls(cls))(data); - return arr; -} -$rt_createUnfilledArray = function(cls, sz) { - return new ($rt_arraycls(cls))(new Array(sz)); -} -$rt_createLongArray = function(sz) { - var data = new Array(sz); - var arr = new ($rt_arraycls($rt_longcls()))(data); - for (var i = 0; i < sz; i = (i + 1) | 0) { - data[i] = Long.ZERO; - } - return arr; -} -if (ArrayBuffer) { - $rt_createNumericArray = function(cls, nativeArray) { - return new ($rt_arraycls(cls))(nativeArray); - } - $rt_createByteArray = function(sz) { - return $rt_createNumericArray($rt_bytecls(), new Int8Array(new ArrayBuffer(sz)), 0); - }; - $rt_createShortArray = function(sz) { - return $rt_createNumericArray($rt_shortcls(), new Int16Array(new ArrayBuffer(sz << 1)), 0); - }; - $rt_createIntArray = function(sz) { - return $rt_createNumericArray($rt_intcls(), new Int32Array(new ArrayBuffer(sz << 2)), 0); - }; - $rt_createBooleanArray = function(sz) { - return $rt_createNumericArray($rt_booleancls(), new Int8Array(new ArrayBuffer(sz)), 0); - }; - $rt_createFloatArray = function(sz) { - return $rt_createNumericArray($rt_floatcls(), new Float32Array(new ArrayBuffer(sz << 2)), 0); - }; - $rt_createDoubleArray = function(sz) { - return $rt_createNumericArray($rt_doublecls(), new Float64Array(new ArrayBuffer(sz << 3)), 0); - }; - $rt_createCharArray = function(sz) { - return $rt_createNumericArray($rt_charcls(), new Uint16Array(new ArrayBuffer(sz << 1)), 0); - }; -} else { - $rt_createNumericArray = function(cls, sz) { - var data = new Array(sz); - var arr = new ($rt_arraycls(cls))(data); - for (var i = 0; i < sz; i = (i + 1) | 0) { - data[i] = 0; - } - return arr; - } - $rt_createByteArray = function(sz) { return $rt_createNumericArray($rt_bytecls(), sz); } - $rt_createShortArray = function(sz) { return $rt_createNumericArray($rt_shortcls(), sz); } - $rt_createIntArray = function(sz) { return $rt_createNumericArray($rt_intcls(), sz); } - $rt_createBooleanArray = function(sz) { return $rt_createNumericArray($rt_booleancls(), sz); } - $rt_createFloatArray = function(sz) { return $rt_createNumericArray($rt_floatcls(), sz); } - $rt_createDoubleArray = function(sz) { return $rt_createNumericArray($rt_doublecls(), sz); } - $rt_createCharArray = function(sz) { return $rt_createNumericArray($rt_charcls(), sz); } -} -$rt_arraycls = function(cls) { - if (cls.$array == undefined) { - var arraycls = function(data) { - this.data = data; - this.$id = $rt_nextId(); - }; - arraycls.prototype = new ($rt_objcls())(); - arraycls.prototype.constructor = arraycls; - arraycls.$meta = { item : cls, supertypes : [$rt_objcls()], primitive : false, superclass : $rt_objcls() }; - cls.$array = arraycls; - } - return cls.$array; -} -$rt_createcls = function() { - return { - $meta : { - supertypes : [] - } - }; -} -$rt_booleanclsCache = null; -$rt_booleancls = function() { - if ($rt_booleanclsCache == null) { - $rt_booleanclsCache = $rt_createcls(); - $rt_booleanclsCache.primitive = true; - $rt_booleanclsCache.name = "boolean"; - } - return $rt_booleanclsCache; -} -$rt_charclsCache = null; -$rt_charcls = function() { - if ($rt_charclsCache == null) { - $rt_charclsCache = $rt_createcls(); - $rt_charclsCache.primitive = true; - $rt_charclsCache.name = "char"; - } - return $rt_charclsCache; -} -$rt_byteclsCache = null; -$rt_bytecls = function() { - if ($rt_byteclsCache == null) { - $rt_byteclsCache = $rt_createcls(); - $rt_byteclsCache.primitive = true; - $rt_byteclsCache.name = "byte"; - } - return $rt_byteclsCache; -} -$rt_shortclsCache = null; -$rt_shortcls = function() { - if ($rt_shortclsCache == null) { - $rt_shortclsCache = $rt_createcls(); - $rt_shortclsCache.primitive = true; - $rt_shortclsCache.name = "short"; - } - return $rt_shortclsCache; -} -$rt_intclsCache = null; -$rt_intcls = function() { - if ($rt_intclsCache === null) { - $rt_intclsCache = $rt_createcls(); - $rt_intclsCache.primitive = true; - $rt_intclsCache.name = "int"; - } - return $rt_intclsCache; -} -$rt_longclsCache = null; -$rt_longcls = function() { - if ($rt_longclsCache === null) { - $rt_longclsCache = $rt_createcls(); - $rt_longclsCache.primitive = true; - $rt_longclsCache.name = "long"; - } - return $rt_longclsCache; -} -$rt_floatclsCache = null; -$rt_floatcls = function() { - if ($rt_floatclsCache === null) { - $rt_floatclsCache = $rt_createcls(); - $rt_floatclsCache.primitive = true; - $rt_floatclsCache.name = "float"; - } - return $rt_floatclsCache; -} -$rt_doubleclsCache = null; -$rt_doublecls = function() { - if ($rt_doubleclsCache === null) { - $rt_doubleclsCache = $rt_createcls(); - $rt_doubleclsCache.primitive = true; - $rt_doubleclsCache.name = "double"; - } - return $rt_doubleclsCache; -} -$rt_voidclsCache = null; -$rt_voidcls = function() { - if ($rt_voidclsCache === null) { - $rt_voidclsCache = $rt_createcls(); - $rt_voidclsCache.primitive = true; - $rt_voidclsCache.name = "void"; - } - return $rt_voidclsCache; -} -$rt_equals = function(a, b) { - if (a === b) { - return true; - } - if (a === null || b === null) { - return false; - } - if (typeof(a) == 'object') { - return a.equals(b); - } else { - return false; - } -} -$rt_clinit = function(cls) { - if (cls.$clinit) { - var f = cls.$clinit; - delete cls.$clinit; - f(); - } - return cls; -} -$rt_init = function(cls, constructor, args) { - var obj = new cls(); - cls.prototype[constructor].apply(obj, args); - return obj; -} -$rt_throw = function(ex) { - var err = ex.$jsException; - if (!err) { - var err = new Error("Java exception thrown"); - err.$javaException = ex; - ex.$jsException = err; - } - throw err; -} -$rt_byteToInt = function(value) { - return value > 0xFF ? value | 0xFFFFFF00 : value; -} -$rt_shortToInt = function(value) { - return value > 0xFFFF ? value | 0xFFFF0000 : value; -} -$rt_createMultiArray = function(cls, dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createArray(cls, firstDim); - } - return $rt_createMultiArrayImpl(cls, arrays, dimensions); -} -$rt_createByteMultiArray = function(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createByteArray(firstDim); - } - return $rt_createMultiArrayImpl($rt_bytecls(), arrays, dimensions); -} -$rt_createBooleanMultiArray = function(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createBooleanArray(firstDim); - } - return $rt_createMultiArrayImpl($rt_booleancls(), arrays, dimensions); -} -$rt_createShortMultiArray = function(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createShortArray(firstDim); - } - return $rt_createMultiArrayImpl($rt_shortcls(), arrays, dimensions); -} -$rt_createIntMultiArray = function(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createIntArray(firstDim); - } - return $rt_createMultiArrayImpl($rt_intcls(), arrays, dimensions); -} -$rt_createLongMultiArray = function(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createLongArray(firstDim); - } - return $rt_createMultiArrayImpl($rt_longcls(), arrays, dimensions); -} -$rt_createFloatMultiArray = function(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createFloatArray(firstDim); - } - return $rt_createMultiArrayImpl($rt_floatcls(), arrays, dimensions); -} -$rt_createDoubleMultiArray = function(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions)); - var firstDim = dimensions[0] | 0; - for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { - arrays[i] = $rt_createDoubleArray(firstDim); - } - return $rt_createMultiArrayImpl($rt_doublecls(), arrays, dimensions); -} -$rt_primitiveArrayCount = function(dimensions) { - var val = dimensions[1] | 0; - for (var i = 2 | 0; i < dimensions.length; i = (i + 1) | 0) { - val = (val * (dimensions[i] | 0)) | 0; - } - return val; -} -$rt_createMultiArrayImpl = function(cls, arrays, dimensions) { - var limit = arrays.length; - for (var i = 1 | 0; i < dimensions.length; i = (i + 1) | 0) { - cls = $rt_arraycls(cls); - var dim = dimensions[i]; - var index = 0; - var packedIndex = 0; - while (index < limit) { - var arr = $rt_createUnfilledArray(cls, dim); - for (var j = 0; j < dim; j = (j + 1) | 0) { - arr.data[j] = arrays[index]; - index = (index + 1) | 0; - } - arrays[packedIndex] = arr; - packedIndex = (packedIndex + 1) | 0; - } - limit = packedIndex; - } - return arrays[0]; -} -$rt_assertNotNaN = function(value) { - if (typeof value == 'number' && isNaN(value)) { - throw "NaN"; - } - return value; -} -$rt_methodStubs = function(clinit, names) { - for (var i = 0; i < names.length; i = (i + 1) | 0) { - window[names[i]] = (function(name) { - return function() { - clinit(); - return window[name].apply(window, arguments); - } - })(names[i]); - } -} -$rt_stdoutBuffer = ""; -$rt_putStdout = function(ch) { - if (ch === 0xA) { - if (console) { - console.info($rt_stdoutBuffer); - } - $rt_stdoutBuffer = ""; - } else { - $rt_stdoutBuffer += String.fromCharCode(ch); - } -} -$rt_stderrBuffer = ""; -$rt_putStderr = function(ch) { - if (ch === 0xA) { - if (console) { - console.info($rt_stderrBuffer); - } - $rt_stderrBuffer = ""; - } else { - $rt_stderrBuffer += String.fromCharCode(ch); - } -} -function $rt_declClass(cls, data) { - cls.name = data.name; - cls.$meta = {}; - cls.$meta.superclass = data.superclass; - cls.$meta.supertypes = data.interfaces ? data.interfaces.slice() : []; - if (data.superclass) { - cls.$meta.supertypes.push(data.superclass); - cls.prototype = new data.superclass(); - } else { - cls.prototype = new Object(); - } - cls.$meta.name = data.name; - cls.$meta.enum = data.enum; - cls.prototype.constructor = cls; - cls.$clinit = data.clinit; -} -function $rt_virtualMethods(cls) { - for (var i = 1; i < arguments.length; i += 2) { - var name = arguments[i]; - var func = arguments[i + 1]; - if (typeof name == 'string') { - cls.prototype[name] = func; - } else { - for (var j = 0; j < name.length; ++j) { - cls.prototype[name[j]] = func; - } - } - } -} - -Long = function(lo, hi) { - this.lo = lo | 0; - this.hi = hi | 0; -} -Long_ZERO = new Long(0, 0); -Long_fromInt = function(val) { - return val >= 0 ? new Long(val, 0) : new Long(val, -1); -} -Long_fromNumber = function(val) { - return new Long(val | 0, (val / 0x100000000) | 0); -} -Long_toNumber = function(val) { - return val.hi >= 0 ? val.lo + 0x100000000 * val.hi : -0x100000000 * (val.hi ^ 0xFFFFFFFF) + val.lo; -} -Long_add = function(a, b) { - var a_lolo = a.lo & 0xFFFF; - var a_lohi = a.lo >>> 16; - var a_hilo = a.hi & 0xFFFF; - var a_hihi = a.hi >>> 16; - var b_lolo = b.lo & 0xFFFF; - var b_lohi = b.lo >>> 16; - var b_hilo = b.hi & 0xFFFF; - var b_hihi = b.hi >>> 16; - - var lolo = (a_lolo + b_lolo) | 0; - var lohi = (a_lohi + b_lohi + (lolo >> 16)) | 0; - var hilo = (a_hilo + b_hilo + (lohi >> 16)) | 0; - var hihi = (a_hihi + b_hihi + (hilo >> 16)) | 0; - return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), - (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16)); -} -Long_inc = function(a) { - var lo = (a.lo + 1) | 0; - var hi = a.hi; - if (lo === 0) { - hi = (hi + 1) | 0; - } - return new Long(lo, hi); -} -Long_dec = function(a) { - var lo = (a.lo - 1) | 0; - var hi = a.hi; - if (lo === -1) { - hi = (hi - 1) | 0; - } - return new Long(lo, hi); -} -Long_neg = function(a) { - return Long_inc(new Long(a.lo ^ 0xFFFFFFFF, a.hi ^ 0xFFFFFFFF)); -} -Long_sub = function(a, b) { - var a_lolo = a.lo & 0xFFFF; - var a_lohi = a.lo >>> 16; - var a_hilo = a.hi & 0xFFFF; - var a_hihi = a.hi >>> 16; - var b_lolo = b.lo & 0xFFFF; - var b_lohi = b.lo >>> 16; - var b_hilo = b.hi & 0xFFFF; - var b_hihi = b.hi >>> 16; - - var lolo = (a_lolo - b_lolo) | 0; - var lohi = (a_lohi - b_lohi + (lolo >> 16)) | 0; - var hilo = (a_hilo - b_hilo + (lohi >> 16)) | 0; - var hihi = (a_hihi - b_hihi + (hilo >> 16)) | 0; - return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), - (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16)); -} -Long_compare = function(a, b) { - var r = a.hi - b.hi; - if (r !== 0) { - return r; - } - var r = (a.lo >>> 1) - (b.lo >>> 1); - if (r !== 0) { - return r; - } - return (a.lo & 1) - (b.lo & 1); -} -Long_isPositive = function(a) { - return (a.hi & 0x80000000) === 0; -} -Long_isNegative = function(a) { - return (a.hi & 0x80000000) !== 0; -} -Long_mul = function(a, b) { - var positive = Long_isNegative(a) === Long_isNegative(b); - if (Long_isNegative(a)) { - a = Long_neg(a); - } - if (Long_isNegative(b)) { - b = Long_neg(b); - } - var a_lolo = a.lo & 0xFFFF; - var a_lohi = a.lo >>> 16; - var a_hilo = a.hi & 0xFFFF; - var a_hihi = a.hi >>> 16; - var b_lolo = b.lo & 0xFFFF; - var b_lohi = b.lo >>> 16; - var b_hilo = b.hi & 0xFFFF; - var b_hihi = b.hi >>> 16; - - var lolo = (a_lolo * b_lolo) | 0; - var lohi = (a_lohi * b_lolo + a_lolo * b_lohi + (lolo >> 16)) | 0; - var hilo = (a_hilo * b_lolo + a_lohi * b_lohi + a_lolo * b_hilo + (lohi >> 16)) | 0; - var hihi = (a_hihi * b_lolo + a_hilo * b_lohi + a_lohi * b_hilo + a_lolo * b_hihi + (hilo >> 16)) | 0; - var result = new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16)); - return positive ? result : Long_neg(result); -} -Long_div = function(a, b) { - return Long_divRem(a, b)[0]; -} -Long_rem = function(a, b) { - return Long_divRem(a, b)[1]; -} -Long_divRem = function(a, b) { - var positive = Long_isNegative(a) === Long_isNegative(b); - if (Long_isNegative(a)) { - a = Long_neg(a); - } - if (Long_isNegative(b)) { - b = Long_neg(b); - } - a = new LongInt(a.lo, a.hi, 0); - b = new LongInt(b.lo, b.hi, 0); - var q = LongInt_div(a, b); - a = new Long(a.lo, a.hi); - q = new Long(q.lo, q.hi); - return positive ? [q, a] : [Long_neg(q), Long_neg(a)]; -} -Long_shiftLeft16 = function(a) { - return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16)); -} -Long_shiftRight16 = function(a) { - return new Long((a.lo >>> 16) | (a.hi << 16), a.hi >>> 16); -} -Long_and = function(a, b) { - return new Long(a.lo & b.lo, a.hi & b.hi); -} -Long_or = function(a, b) { - return new Long(a.lo | b.lo, a.hi | b.hi); -} -Long_xor = function(a, b) { - return new Long(a.lo ^ b.lo, a.hi ^ b.hi); -} -Long_shl = function(a, b) { - if (b < 32) { - return new Long(a.lo << b, (a.lo >>> (32 - b)) | (a.hi << b)); - } else { - return new Long(0, a.lo << (b - 32)); - } -} -Long_shr = function(a, b) { - if (b < 32) { - return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >> b); - } else { - return new Long((a.hi >> (b - 32)), -1); - } -} -Long_shru = function(a, b) { - if (b < 32) { - return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >>> b); - } else { - return new Long((a.hi >>> (b - 32)), 0); - } -} - -// Represents a mutable 80-bit unsigned integer -LongInt = function(lo, hi, sup) { - this.lo = lo; - this.hi = hi; - this.sup = sup; -} -LongInt_mul = function(a, b) { - var a_lolo = ((a.lo & 0xFFFF) * b) | 0; - var a_lohi = ((a.lo >>> 16) * b) | 0; - var a_hilo = ((a.hi & 0xFFFF) * b) | 0; - var a_hihi = ((a.hi >>> 16) * b) | 0; - var sup = (a.sup * b) | 0; - - a_lohi = (a_lohi + (a_lolo >> 16)) | 0; - a_hilo = (a_hilo + (a_lohi >> 16)) | 0; - a_hihi = (a_hihi + (a_hilo >> 16)) | 0; - sup = (sup + (a_hihi >> 16)) | 0; - a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16); - a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16); - a.sup = sup & 0xFFFF; -} -LongInt_sub = function(a, b) { - var a_lolo = a.lo & 0xFFFF; - var a_lohi = a.lo >>> 16; - var a_hilo = a.hi & 0xFFFF; - var a_hihi = a.hi >>> 16; - var b_lolo = b.lo & 0xFFFF; - var b_lohi = b.lo >>> 16; - var b_hilo = b.hi & 0xFFFF; - var b_hihi = b.hi >>> 16; - - a_lolo = (a_lolo - b_lolo) | 0; - a_lohi = (a_lohi - b_lohi + (a_lolo >> 16)) | 0; - a_hilo = (a_hilo - b_hilo + (a_lohi >> 16)) | 0; - a_hihi = (a_hihi - b_hihi + (a_hilo >> 16)) | 0; - sup = (a.sup - b.sup + (a_hihi >> 16)) | 0; - a.lo = (a_lolo & 0xFFFF) | ((a_lohi & 0xFFFF) << 16); - a.hi = (a_hilo & 0xFFFF) | ((a_hihi & 0xFFFF) << 16); - a.sup = sup; -} -LongInt_add = function(a, b) { - var a_lolo = a.lo & 0xFFFF; - var a_lohi = a.lo >>> 16; - var a_hilo = a.hi & 0xFFFF; - var a_hihi = a.hi >>> 16; - var b_lolo = b.lo & 0xFFFF; - var b_lohi = b.lo >>> 16; - var b_hilo = b.hi & 0xFFFF; - var b_hihi = b.hi >>> 16; - - a_lolo = (a_lolo + b_lolo) | 0; - a_lohi = (a_lohi + b_lohi + (a_lolo >> 16)) | 0; - a_hilo = (a_hilo + b_hilo + (a_lohi >> 16)) | 0; - a_hihi = (a_hihi + b_hihi + (a_hilo >> 16)) | 0; - sup = (a.sup + b.sup + (a_hihi >> 16)) | 0; - a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16); - a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16); - a.sup = sup; -} -LongInt_ucompare = function(a, b) { - var r = (a.sup - b.sup); - if (r != 0) { - return r; - } - var r = (a.hi >>> 1) - (b.hi >>> 1); - if (r != 0) { - return r; - } - var r = (a.hi & 1) - (b.hi & 1); - if (r != 0) { - return r; - } - var r = (a.lo >>> 1) - (b.lo >>> 1); - if (r != 0) { - return r; - } - return (a.lo & 1) - (b.lo & 1); -} -LongInt_numOfLeadingZeroBits = function(a) { - var n = 0; - var d = 16; - while (d > 0) { - if ((a >>> d) !== 0) { - a >>>= d; - n = (n + d) | 0; - } - d = (d / 2) | 0; - } - return 31 - n; -} -LongInt_shl = function(a, b) { - if (b < 32) { - a.sup = ((a.hi >>> (32 - b)) | (a.sup << b)) & 0xFFFF; - a.hi = (a.lo >>> (32 - b)) | (a.hi << b); - a.lo <<= b; - } else if (b < 64) { - a.sup = ((a.lo >>> (64 - b)) | (a.hi << (b - 32))) & 0xFFFF; - a.hi = a.lo << b; - a.lo = 0; - } else { - a.sup = (a.lo << (b - 64)) & 0xFFFF; - a.hi = 0; - a.lo = 0; - } -} -LongInt_shr = function(a, b) { - if (b < 32) { - a.lo = (a.lo >>> b) | (a.hi << (32 - b)); - a.hi = (a.hi >>> b) | (a.sup << (32 - b)); - a.sup >>>= b; - } else if (b < 64) { - a.lo = (a.hi >>> (b - 32)) | (a.sup << (64 - b)); - a.hi = a.sup >>> (b - 32); - a.sup = 0; - } else { - a.lo = a.sup >>> (b - 64); - a.hi = 0; - a.sup = 0; - } -} -LongInt_copy = function(a) { - return new LongInt(a.lo, a.hi, a.sup); -} -LongInt_div = function(a, b) { - // Normalize divisor - var bits = b.hi !== 0 ? LongInt_numOfLeadingZeroBits(b.hi) : LongInt_numOfLeadingZeroBits(b.lo) + 32; - var sz = 1 + ((bits / 16) | 0); - var dividentBits = bits % 16; - LongInt_shl(b, bits); - LongInt_shl(a, dividentBits); - q = new LongInt(0, 0, 0); - while (sz-- > 0) { - LongInt_shl(q, 16); - // Calculate approximate q - var digitA = (a.hi >>> 16) + (0x10000 * a.sup); - var digitB = b.hi >>> 16; - var digit = (digitA / digitB) | 0; - var t = LongInt_copy(b); - LongInt_mul(t, digit); - // Adjust q either down or up - if (LongInt_ucompare(t, a) >= 0) { - while (LongInt_ucompare(t, a) > 0) { - LongInt_sub(t, b); - q = (q - 1) | 0; - } - } else { - while (true) { - var nextT = LongInt_copy(t); - LongInt_add(nextT, b); - if (LongInt_ucompare(nextT, a) > 0) { - break; - } - t = nextT; - q = (q + 1) | 0; - } - } - LongInt_sub(a, t); - q.lo |= digit; - LongInt_shl(a, 16); - } - LongInt_shr(a, bits + 16); - return q; -} \ No newline at end of file diff --git a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java index fca37cc4a..dc1fa0fc3 100644 --- a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java +++ b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java @@ -55,5 +55,18 @@ public final class AsyncProgram { } } System.out.println("Complete async"); + + System.out.println("Throwing exception"); + try { + throwException(); + } catch (IllegalStateException e) { + System.out.println("Exception caught"); + } + } + + private static void throwException() { + Thread.yield(); + System.out.println("Thread.yield called"); + throw new IllegalStateException(); } } diff --git a/teavm-samples/teavm-samples-async/src/main/webapp/index.html b/teavm-samples/teavm-samples-async/src/main/webapp/index.html index 96817c4a0..9f7151523 100644 --- a/teavm-samples/teavm-samples-async/src/main/webapp/index.html +++ b/teavm-samples/teavm-samples-async/src/main/webapp/index.html @@ -21,7 +21,7 @@ - +

Please, open developer's console to view program's output

\ No newline at end of file From 5dfc8a3ed6b181f0b197451ddc3c6131ee7ac8b0 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Fri, 6 Feb 2015 00:53:51 +0400 Subject: [PATCH 2/6] Return back AST optimization of async methods. --- .../java/org/teavm/javascript/Decompiler.java | 4 +--- .../main/java/org/teavm/javascript/Optimizer.java | 15 +++++++++------ .../teavm/javascript/ReadWriteStatsBuilder.java | 11 +++++++++++ .../main/java/org/teavm/javascript/Renderer.java | 15 ++++++++------- .../resources/org/teavm/javascript/runtime.js | 9 +++++++++ .../org/teavm/samples/async/AsyncProgram.java | 13 ------------- 6 files changed, 38 insertions(+), 29 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index affd53dd4..477eccc0f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -202,7 +202,6 @@ public class Decompiler { AsyncMethodNode node = new AsyncMethodNode(method.getReference()); AsyncProgramSplitter splitter = new AsyncProgramSplitter(asyncMethods); splitter.split(method.getProgram()); - List partPrograms = new ArrayList<>(); for (int i = 0; i < splitter.size(); ++i) { Integer input = null; if (i > 0) { @@ -214,14 +213,13 @@ public class Decompiler { AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i), input); node.getBody().add(part); - partPrograms.add(splitter.getProgram(i)); } Program program = method.getProgram(); for (int i = 0; i < program.variableCount(); ++i) { node.getVariables().add(program.variableAt(i).getRegister()); } Optimizer optimizer = new Optimizer(); - optimizer.optimize(node, partPrograms); + optimizer.optimize(node, program, splitter); node.getModifiers().addAll(mapModifiers(method.getModifiers())); int paramCount = Math.min(method.getSignature().length, program.variableCount()); for (int i = 0; i < paramCount; ++i) { diff --git a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java index 9e0d53395..f69803a48 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java @@ -15,12 +15,11 @@ */ package org.teavm.javascript; -import java.util.List; import org.teavm.javascript.ast.AsyncMethodNode; import org.teavm.javascript.ast.AsyncMethodPart; import org.teavm.javascript.ast.RegularMethodNode; import org.teavm.model.Program; - +import org.teavm.model.util.AsyncProgramSplitter; /** * @@ -44,13 +43,17 @@ public class Optimizer { } } - public void optimize(AsyncMethodNode method, List programs) { + public void optimize(AsyncMethodNode method, Program program, AsyncProgramSplitter splitter) { ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size()); - for (Program program : programs) { - stats.analyze(program); + stats.analyze(program); + for (int i = 0; i < splitter.size(); ++i) { + Integer var = splitter.getInput(i); + if (var != null) { + stats.reads[var]++; + } } - OptimizingVisitor optimizer = new OptimizingVisitor(stats); for (AsyncMethodPart part : method.getBody()) { + OptimizingVisitor optimizer = new OptimizingVisitor(stats.copy()); part.getStatement().acceptVisitor(optimizer); part.setStatement(optimizer.resultStmt); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java index 9be54242a..0e39dd41c 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java @@ -15,6 +15,7 @@ */ package org.teavm.javascript; +import java.util.Arrays; import org.teavm.model.*; import org.teavm.model.util.DefinitionExtractor; import org.teavm.model.util.UsageExtractor; @@ -27,11 +28,21 @@ class ReadWriteStatsBuilder { public int[] reads; public int[] writes; + private ReadWriteStatsBuilder() { + } + public ReadWriteStatsBuilder(int variableCount) { reads = new int[variableCount]; writes = new int[variableCount]; } + public ReadWriteStatsBuilder copy() { + ReadWriteStatsBuilder result = new ReadWriteStatsBuilder(); + result.reads = Arrays.copyOf(reads, reads.length); + result.writes = Arrays.copyOf(writes, writes.length); + return result; + } + public void analyze(Program program) { DefinitionExtractor defExtractor = new DefinitionExtractor(); UsageExtractor useExtractor = new UsageExtractor(); diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 545a26032..4d0dc05df 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -533,7 +533,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } writer.append(")").ws().append("{").softNewLine().indent(); method.acceptVisitor(new MethodBodyRenderer()); - writer.outdent().append("}").newLine(); + writer.outdent().append("}"); + if (inner) { + writer.append(';'); + } + writer.newLine(); debugEmitter.emitMethod(null); } @@ -630,18 +634,15 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append(";").softNewLine(); } for (int i = 0; i < methodNode.getBody().size(); ++i) { - writer.append("function $part_").append(i).append("("); + writer.append("var $part_").append(i).ws().append("=").ws().append("$rt_guardAsync(function("); if (i > 0) { writer.append("$restore"); } - writer.append(")").ws().append('{').indent().softNewLine(); - writer.append("try {").indent().softNewLine(); + writer.append(")").ws().append("{").indent().softNewLine(); AsyncMethodPart part = methodNode.getBody().get(i); part.getStatement().acceptVisitor(Renderer.this); - writer.outdent().append("} catch ($guard) {").indent().softNewLine(); writer.append("return $return($rt_asyncError($guard));").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.outdent().append("}").softNewLine(); + writer.outdent().append("},").ws().append("$return);").softNewLine(); } writer.append("return $part_0();").softNewLine(); } catch (IOException e) { diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index 0fd948135..8cf0b7f10 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -455,6 +455,15 @@ function $rt_continue(f) { return f; } } +function $rt_guardAsync(f, continuation) { + return function() { + try { + return f.apply(this, arguments); + } catch (e) { + return continuation($rt_asyncError(e)); + } + } +} function $dbg_repr(obj) { return obj.toString ? obj.toString() : ""; diff --git a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java index dc1fa0fc3..fca37cc4a 100644 --- a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java +++ b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java @@ -55,18 +55,5 @@ public final class AsyncProgram { } } System.out.println("Complete async"); - - System.out.println("Throwing exception"); - try { - throwException(); - } catch (IllegalStateException e) { - System.out.println("Exception caught"); - } - } - - private static void throwException() { - Thread.yield(); - System.out.println("Thread.yield called"); - throw new IllegalStateException(); } } From de7dc645bcd71df809d5979970e5685e2016b86f Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Fri, 6 Feb 2015 01:56:39 +0400 Subject: [PATCH 3/6] Add wielding of try/catch statements --- .../teavm/javascript/OptimizingVisitor.java | 36 +++++++++++++++++++ .../java/org/teavm/javascript/Renderer.java | 1 - .../org/teavm/samples/async/AsyncProgram.java | 13 +++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java index 2d57ee859..03eb93654 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java @@ -321,9 +321,45 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { return false; } } + wieldTryCatch(statements); return true; } + private void wieldTryCatch(List statements) { + for (int i = 0; i < statements.size() - 1; ++i) { + if (statements.get(i) instanceof TryCatchStatement && statements.get(i + 1) instanceof TryCatchStatement) { + TryCatchStatement first = (TryCatchStatement)statements.get(i); + TryCatchStatement second = (TryCatchStatement)statements.get(i + 1); + if (Objects.equals(first.getExceptionType(), second.getExceptionType()) && + Objects.equals(first.getExceptionVariable(), second.getExceptionVariable()) && + briefStatementComparison(first.getHandler(), second.getHandler())) { + first.getProtectedBody().addAll(second.getProtectedBody()); + statements.remove(i + 1); + wieldTryCatch(first.getProtectedBody()); + --i; + continue; + } + } + } + } + + private boolean briefStatementComparison(List firstSeq, List secondSeq) { + if (firstSeq.isEmpty() && secondSeq.isEmpty()) { + return true; + } + if (firstSeq.size() != 1 || secondSeq.size() != 1) { + return false; + } + Statement first = firstSeq.get(0); + Statement second = secondSeq.get(0); + if (first instanceof BreakStatement && second instanceof BreakStatement) { + BreakStatement firstBreak = (BreakStatement)first; + BreakStatement secondBreak = (BreakStatement)second; + return firstBreak.getTarget() == secondBreak.getTarget(); + } + return false; + } + private void eliminateRedundantBreaks(List statements, IdentifiedStatement exit) { if (statements.isEmpty()) { return; diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 4d0dc05df..490aa00b2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -641,7 +641,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append(")").ws().append("{").indent().softNewLine(); AsyncMethodPart part = methodNode.getBody().get(i); part.getStatement().acceptVisitor(Renderer.this); - writer.append("return $return($rt_asyncError($guard));").softNewLine(); writer.outdent().append("},").ws().append("$return);").softNewLine(); } writer.append("return $part_0();").softNewLine(); diff --git a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java index fca37cc4a..dc1fa0fc3 100644 --- a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java +++ b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java @@ -55,5 +55,18 @@ public final class AsyncProgram { } } System.out.println("Complete async"); + + System.out.println("Throwing exception"); + try { + throwException(); + } catch (IllegalStateException e) { + System.out.println("Exception caught"); + } + } + + private static void throwException() { + Thread.yield(); + System.out.println("Thread.yield called"); + throw new IllegalStateException(); } } From 484bf61a5c3bb49cb1bb7e153a55c9692d38e8d5 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 6 Feb 2015 18:01:20 +0400 Subject: [PATCH 4/6] Fix wielding of try/catch blocks --- .../src/main/java/org/teavm/javascript/OptimizingVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java index 03eb93654..3d432424b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java @@ -289,6 +289,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { List backup = resultSequence; resultSequence = new ArrayList<>(); processSequenceImpl(statements); + wieldTryCatch(resultSequence); List result = new ArrayList<>(); for (Statement part : resultSequence) { if (part != null) { @@ -321,7 +322,6 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { return false; } } - wieldTryCatch(statements); return true; } From 1f8ef1092ca53514f9d439f509e17e88eb1d3024 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 6 Feb 2015 18:51:42 +0400 Subject: [PATCH 5/6] Support async code in junit --- .../teavm/classlib/java/lang/ThreadTest.java | 48 ++++++++++++++++ .../java/org/teavm/tooling/TeaVMTestTool.java | 2 +- .../java/org/teavm/tooling/TeaVMTool.java | 7 ++- .../src/main/java/org/teavm/vm/TeaVM.java | 15 ++++- .../java/org/teavm/vm/TeaVMEntryPoint.java | 10 ++++ .../resources/org/teavm/javascript/runtime.js | 6 +- .../teavm/tooling/test/res/junit-support.js | 55 +++++++++++-------- 7 files changed, 111 insertions(+), 32 deletions(-) create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java new file mode 100644 index 000000000..22f7230bd --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java @@ -0,0 +1,48 @@ +/* + * 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.classlib.java.lang; + +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class ThreadTest { + @Test + public void sleeps() throws InterruptedException { + long start = System.currentTimeMillis(); + Thread.sleep(100); + long duration = System.currentTimeMillis() - start; + assertTrue("Thread.sleed did not wait enogh", duration < 100); + } + + @Test + public void catchesAsyncException() { + try { + throwException(); + fail("Exception should have been thrown"); + } catch (IllegalStateException e) { + // all is ok + } + } + + private void throwException() { + Thread.yield(); + throw new IllegalStateException(); + } +} diff --git a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java index cc7867722..1bf954c91 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java @@ -361,7 +361,7 @@ public class TeaVMTestTool { MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException", Throwable.class, String.class); vm.entryPoint("initInstance", cons); - vm.entryPoint("runTest", methodRef).withValue(0, cons.getClassName()); + vm.entryPoint("runTest", methodRef).withValue(0, cons.getClassName()).async(); vm.entryPoint("extractException", exceptionMsg); vm.exportType("TestClass", cons.getClassName()); vm.setDebugEmitter(debugInfoBuilder); diff --git a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java index 7e9b1cd9b..2e432347b 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -264,7 +264,7 @@ public class TeaVMTool { if (mainClass != null) { MethodDescriptor mainMethodDesc = new MethodDescriptor("main", String[].class, void.class); vm.entryPoint("main", new MethodReference(mainClass, mainMethodDesc)) - .withValue(1, "java.lang.String"); + .withValue(1, "java.lang.String").async(); } for (ClassAlias alias : classAliases) { vm.exportType(alias.getAlias(), alias.getClassName()); @@ -272,7 +272,7 @@ public class TeaVMTool { for (MethodAlias methodAlias : methodAliases) { MethodReference ref = new MethodReference(methodAlias.getClassName(), methodAlias.getMethodName(), MethodDescriptor.parseSignature(methodAlias.getDescriptor())); - TeaVMEntryPoint entryPoint = vm.entryPoint(methodAlias.getAlias(), ref); + TeaVMEntryPoint entryPoint = vm.entryPoint(methodAlias.getAlias(), ref).async(); if (methodAlias.getTypes() != null) { for (int i = 0; i < methodAlias.getTypes().length; ++i) { String types = methodAlias.getTypes()[i]; @@ -299,6 +299,9 @@ public class TeaVMTool { cancelled = true; return; } + if (mainClass != null) { + writer.append("main = $rt_rootInvocationAdapter(main);\n"); + } ProblemProvider problemProvider = vm.getProblemProvider(); if (problemProvider.getProblems().isEmpty()) { log.info("JavaScript file successfully built"); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 7a03aa30b..6f974e1e1 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -89,6 +89,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private TeaVMProgressListener progressListener; private boolean cancelled; private ListableClassHolderSource writtenClasses; + private Set asyncMethods = new HashSet<>(); TeaVM(ClassReaderSource classSource, ClassLoader classLoader) { this.classSource = classSource; @@ -435,9 +436,16 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } renderer.renderStringPool(); for (Map.Entry entry : entryPoints.entrySet()) { - sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws() - .append("$rt_rootInvocationAdapter(") - .appendMethodBody(entry.getValue().reference).append(");").softNewLine(); + sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws(); + boolean wrapAsync = !asyncMethods.contains(entry.getValue().reference) && entry.getValue().isAsync(); + if (wrapAsync) { + sourceWriter.append("$rt_asyncAdapter("); + } + sourceWriter.appendMethodBody(entry.getValue().reference); + if (wrapAsync) { + sourceWriter.append(")"); + } + sourceWriter.append(";").softNewLine(); } for (Map.Entry entry : exportedClasses.entrySet()) { sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws() @@ -529,6 +537,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private List modelToAst(ListableClassHolderSource classes) { AsyncMethodFinder asyncFinder = new AsyncMethodFinder(dependencyChecker.getCallGraph(), diagnostics); asyncFinder.find(classes); + asyncMethods.addAll(asyncMethods); progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size()); Decompiler decompiler = new Decompiler(classes, classLoader, asyncFinder.getAsyncMethods()); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java b/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java index ca6b948d1..bcd0cf12e 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java @@ -72,6 +72,7 @@ public class TeaVMEntryPoint { private String publicName; MethodReference reference; private MethodDependency method; + private boolean async; TeaVMEntryPoint(String publicName, MethodReference reference, MethodDependency method) { this.publicName = publicName; @@ -84,6 +85,10 @@ public class TeaVMEntryPoint { return publicName; } + boolean isAsync() { + return async; + } + public TeaVMEntryPoint withValue(int argument, String type) { if (argument > reference.parameterCount()) { throw new IllegalArgumentException("Illegal argument #" + argument + " of " + reference.parameterCount()); @@ -91,4 +96,9 @@ public class TeaVMEntryPoint { method.getVariable(argument).propagate(method.getDependencyAgent().getType(type)); return this; } + + public TeaVMEntryPoint async() { + this.async = true; + return this; + } } diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index 8cf0b7f10..d6793042b 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -411,12 +411,12 @@ function $rt_asyncAdapter(f) { try { result = f.apply(this, args); } catch (e) { - return $rt_asyncError(e); + return $return($rt_asyncError(e)); } - return $rt_asyncResult(result); + return $return($rt_asyncResult(result)); } } -function $rt_rootInvocationAdapter(f, extraArgs) { +function $rt_rootInvocationAdapter(f) { return function() { var args = Array.prototype.slice.apply(arguments); if (extraArgs) { diff --git a/teavm-core/src/main/resources/org/teavm/tooling/test/res/junit-support.js b/teavm-core/src/main/resources/org/teavm/tooling/test/res/junit-support.js index c96d914ab..411fca35a 100644 --- a/teavm-core/src/main/resources/org/teavm/tooling/test/res/junit-support.js +++ b/teavm-core/src/main/resources/org/teavm/tooling/test/res/junit-support.js @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -JUnitServer = function() { +"use strict"; +function JUnitServer() { this.tree = new Tree(document.getElementById("test-tree")); this.totalTimeSpent = 0; this.expectedExceptions = []; @@ -113,11 +114,10 @@ JUnitServer.prototype.runTest = function(node, callback) { this.currentTestNode = node; var self = this; this.loadCode(node.testCase.script, node.testCase.additionalScripts, function() { - messageHandler = function(event) { + function messageHandler(event) { window.removeEventListener("message", messageHandler); var timeSpent = new Date().getTime() - startTime; - node.timeIndicator.appendChild(document.createTextNode( - "(" + (timeSpent / 1000).toFixed(3) + ")")); + node.timeIndicator.appendChild(document.createTextNode("(" + (timeSpent / 1000).toFixed(3) + ")")); self.handleEvent(JSON.parse(event.data), callback); }; window.addEventListener("message", messageHandler); @@ -134,8 +134,7 @@ JUnitServer.prototype.runTest = function(node, callback) { break; } } - node.indicator.className = "complete-indicator " + - (node.success ? "successfull" : "failed"); + node.indicator.className = "complete-indicator " + (node.success ? "successfull" : "failed"); if (!node.success) { node.open(); } @@ -253,7 +252,7 @@ JUnitServer.prototype.cleanupNode = function(node) { } } -Tree = function(container) { +function Tree(container) { this.container = container; this.nodes = []; this.selectedNode = null; @@ -288,7 +287,7 @@ Tree.prototype.getNodes = function() { Tree.prototype.addSelectionListener = function(listener) { this.selectionListeners.push(listener); } -TreeNode = function(elem, content, button, children, tree) { +function TreeNode(elem, content, button, children, tree) { this.elem = elem; this.content = content; this.button = button; @@ -367,31 +366,41 @@ TreeNode.prototype.select = function() { } } -JUnitClient = {}; +var JUnitClient = {}; JUnitClient.run = function() { var handler = window.addEventListener("message", function() { window.removeEventListener("message", handler); - var message = {}; try { var instance = new TestClass(); initInstance(instance); - runTest(instance); - message.status = "ok"; + runTest(instance, function(restore) { + var message = {}; + try { + var result = restore(); + message.status = "ok"; + } catch (e) { + JUnitClient.makeErrorMessage(message, e); + } + window.parent.postMessage(JSON.stringify(message), "*"); + }); } catch (e) { - message.status = "exception"; - if (e.$javaException && e.$javaException.constructor.$meta) { - message.exception = e.$javaException.constructor.$meta.name; - message.stack = e.$javaException.constructor.$meta.name + ": "; - var exceptionMessage = extractException(e.$javaException); - message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : ""; - message.stack += "\n" + e.stack; - } else { - message.stack = e.stack; - } + JUnitClient.makeErrorMessage(message, e); + window.parent.postMessage(JSON.stringify(message), "*"); } - window.parent.postMessage(JSON.stringify(message), "*"); }); } +JUnitClient.makeErrorMessage = function(message, e) { + message.status = "exception"; + if (e.$javaException && e.$javaException.constructor.$meta) { + message.exception = e.$javaException.constructor.$meta.name; + message.stack = e.$javaException.constructor.$meta.name + ": "; + var exceptionMessage = extractException(e.$javaException); + message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : ""; + message.stack += "\n" + e.stack; + } else { + message.stack = e.stack; + } +} JUnitClient.reportError = function(error) { var handler = window.addEventListener("message", function() { window.removeEventListener("message", handler); From ce2c625f53e331414ce3e4b634f8be3cecd1bd63 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 6 Feb 2015 19:02:14 +0400 Subject: [PATCH 6/6] Add test to check async virtual call resolution. Fix bug in async unit tests support --- .../teavm/classlib/java/lang/ThreadTest.java | 30 ++++++++++++++++++- .../java/org/teavm/javascript/Renderer.java | 4 ++- .../src/main/java/org/teavm/vm/TeaVM.java | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java index 22f7230bd..72e4b697c 100644 --- a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java @@ -16,6 +16,8 @@ package org.teavm.classlib.java.lang; import static org.junit.Assert.*; +import java.util.ArrayList; +import java.util.List; import org.junit.Test; /** @@ -28,7 +30,7 @@ public class ThreadTest { long start = System.currentTimeMillis(); Thread.sleep(100); long duration = System.currentTimeMillis() - start; - assertTrue("Thread.sleed did not wait enogh", duration < 100); + assertTrue("Thread.sleed did not wait enogh", duration >= 100); } @Test @@ -41,8 +43,34 @@ public class ThreadTest { } } + private void throwException() { Thread.yield(); throw new IllegalStateException(); } + + @Test + public void asyncVirtualCallsSupported() { + List alist = new ArrayList<>(); + alist.add(new A() { + @Override int foo() { + return 3; + } + }); + alist.add(new A() { + @Override int foo() { + Thread.yield(); + return 5; + } + }); + int sum = 0; + for (A a : alist) { + sum += a.foo(); + } + assertEquals(8, sum); + } + + abstract class A { + abstract int foo(); + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 490aa00b2..d71d3455b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -923,7 +923,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append(" $return($rt_asyncResult("); } if (statement.getResult() != null) { - writer.append(' '); + if (!async) { + writer.append(' '); + } prevCallSite = debugEmitter.emitCallSite(); statement.getResult().acceptVisitor(this); debugEmitter.emitCallSite(); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 6f974e1e1..d7ae3665e 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -537,7 +537,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private List modelToAst(ListableClassHolderSource classes) { AsyncMethodFinder asyncFinder = new AsyncMethodFinder(dependencyChecker.getCallGraph(), diagnostics); asyncFinder.find(classes); - asyncMethods.addAll(asyncMethods); + asyncMethods.addAll(asyncFinder.getAsyncMethods()); progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size()); Decompiler decompiler = new Decompiler(classes, classLoader, asyncFinder.getAsyncMethods());