From 32deaf271612d8ae3ac4b69227b4ced915794293 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 10 Mar 2015 15:19:54 +0400 Subject: [PATCH] Further work on a new thread emulator --- .../org/teavm/classlib/java/lang/TThread.java | 1 + .../src/main/java/org/teavm/cache/AstIO.java | 41 +++-- .../cache/DiskRegularMethodNodeCache.java | 5 +- .../javascript/BreakToContinueReplacer.java | 4 +- .../javascript/CertainBlockCountVisitor.java | 4 +- .../java/org/teavm/javascript/Decompiler.java | 21 +-- .../javascript/NameFrequencyEstimator.java | 2 +- .../java/org/teavm/javascript/Optimizer.java | 9 +- .../teavm/javascript/OptimizingVisitor.java | 8 +- .../javascript/RedundantLabelEliminator.java | 4 +- .../javascript/ReferenceCountingVisitor.java | 4 +- .../java/org/teavm/javascript/Renderer.java | 51 +++--- .../teavm/javascript/StatementGenerator.java | 16 +- .../org/teavm/javascript/TryCatchFinder.java | 4 +- .../javascript/UnusedVariableEliminator.java | 5 +- ...cStatement.java => GotoPartStatement.java} | 12 +- .../teavm/javascript/ast/InvocationExpr.java | 10 -- .../teavm/javascript/ast/RenamingVisitor.java | 9 +- .../javascript/ast/StatementVisitor.java | 6 +- .../model/util/AsyncProgramSplitter.java | 12 +- .../java/org/teavm/tooling/TeaVMTool.java | 2 +- .../resources/org/teavm/javascript/runtime.js | 153 +++++------------- .../java/org/teavm/platform/Platform.java | 10 +- .../platform/plugin/AsyncMethodGenerator.java | 23 ++- .../plugin/NewInstanceDependencySupport.java | 2 +- .../platform/plugin/PlatformGenerator.java | 104 ++++-------- teavm-samples/teavm-samples-async/pom.xml | 4 +- 27 files changed, 193 insertions(+), 333 deletions(-) rename teavm-core/src/main/java/org/teavm/javascript/ast/{RestoreAsyncStatement.java => GotoPartStatement.java} (78%) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java index 2576f0653..a635bc045 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java @@ -80,6 +80,7 @@ public class TThread extends TObject implements TRunnable { currentThread.timeSliceStart = System.currentTimeMillis(); } } + static TThread getMainThread(){ return mainThread; } 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 e519cc896..b1ead2777 100644 --- a/teavm-core/src/main/java/org/teavm/cache/AstIO.java +++ b/teavm-core/src/main/java/org/teavm/cache/AstIO.java @@ -305,10 +305,31 @@ public class AstIO { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { try { output.writeByte(17); - output.writeShort(statement.getReceiver() != null ? statement.getReceiver() : -1); + output.writeShort(statement.getPart()); + } catch (IOException e) { + throw new IOExceptionWrapper(e); + } + } + + @Override + public void visit(MonitorEnterStatement statement) { + try { + output.writeByte(18); + output.writeShort(statement.getAsyncTarget() != null ? 0 : statement.getAsyncTarget()); + writeExpr(statement.getObjectRef()); + } catch (IOException e) { + throw new IOExceptionWrapper(e); + } + } + + @Override + public void visit(MonitorExitStatement statement) { + try { + output.writeByte(19); + writeExpr(statement.getObjectRef()); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -506,16 +527,6 @@ public class AstIO { throw new IOExceptionWrapper(e); } } - - @Override - public void visit(MonitorEnterStatement statement) { - - } - - @Override - public void visit(MonitorExitStatement statement) { - - } } private NodeLocation readLocation(DataInput input) throws IOException { @@ -670,11 +681,11 @@ public class AstIO { return stmt; } case 17: { - short var = input.readShort(); - RestoreAsyncStatement stmt = new RestoreAsyncStatement(); - stmt.setReceiver(var >= 0 ? (int)var : null); + GotoPartStatement stmt = new GotoPartStatement(); + stmt.setPart(input.readShort()); return stmt; } + // TODO: MonitorEnter/MonitorExit 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 c597c6490..bb6d058e0 100644 --- a/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java +++ b/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java @@ -259,17 +259,16 @@ public class DiskRegularMethodNodeCache implements RegularMethodNodeCache { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { } @Override public void visit(MonitorEnterStatement statement) { - + } @Override public void visit(MonitorExitStatement statement) { - } } 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 01e8d7a73..f80c3f981 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/BreakToContinueReplacer.java @@ -114,16 +114,14 @@ class BreakToContinueReplacer implements StatementVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { } @Override public void visit(MonitorEnterStatement statement) { - } @Override public void visit(MonitorExitStatement 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 66152cb01..11702da38 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/CertainBlockCountVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/CertainBlockCountVisitor.java @@ -109,16 +109,14 @@ class CertainBlockCountVisitor implements StatementVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { } @Override public void visit(MonitorEnterStatement statement) { - } @Override public void visit(MonitorExitStatement 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 33d3c3311..88c3745fb 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -203,15 +203,7 @@ public class Decompiler { AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods); splitter.split(method.getProgram()); for (int i = 0; i < splitter.size(); ++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); + AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i)); node.getBody().add(part); } Program program = method.getProgram(); @@ -219,7 +211,7 @@ public class Decompiler { node.getVariables().add(program.variableAt(i).getRegister()); } Optimizer optimizer = new Optimizer(); - optimizer.optimize(node, program, splitter); + optimizer.optimize(node, program); node.getModifiers().addAll(mapModifiers(method.getModifiers())); int paramCount = Math.min(method.getSignature().length, program.variableCount()); for (int i = 0; i < paramCount; ++i) { @@ -234,7 +226,7 @@ public class Decompiler { Program program = method.getProgram(); int[] targetBlocks = new int[program.basicBlockCount()]; Arrays.fill(targetBlocks, -1); - methodNode.setBody(getRegularMethodStatement(program, targetBlocks, null).getStatement()); + methodNode.setBody(getRegularMethodStatement(program, targetBlocks).getStatement()); for (int i = 0; i < program.variableCount(); ++i) { methodNode.getVariables().add(program.variableAt(i).getRegister()); } @@ -249,7 +241,7 @@ public class Decompiler { return methodNode; } - private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks, Integer inputVar) { + private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks) { AsyncMethodPart result = new AsyncMethodPart(); lastBlockId = 1; graph = ProgramUtils.buildControlFlowGraph(program); @@ -302,11 +294,6 @@ 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; diff --git a/teavm-core/src/main/java/org/teavm/javascript/NameFrequencyEstimator.java b/teavm-core/src/main/java/org/teavm/javascript/NameFrequencyEstimator.java index 278e9e642..fdfade618 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/NameFrequencyEstimator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/NameFrequencyEstimator.java @@ -188,7 +188,7 @@ public class NameFrequencyEstimator implements StatementVisitor, ExprVisitor, Me } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { } @Override 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 f69803a48..e32c26b37 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java @@ -19,7 +19,6 @@ 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; /** * @@ -43,15 +42,9 @@ public class Optimizer { } } - public void optimize(AsyncMethodNode method, Program program, AsyncProgramSplitter splitter) { + public void optimize(AsyncMethodNode method, Program program) { ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size()); stats.analyze(program); - for (int i = 0; i < splitter.size(); ++i) { - Integer var = splitter.getInput(i); - if (var != null) { - stats.reads[var]++; - } - } for (AsyncMethodPart part : method.getBody()) { OptimizingVisitor optimizer = new OptimizingVisitor(stats.copy()); part.getStatement().acceptVisitor(optimizer); 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 0d3474098..f155919c4 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java @@ -180,7 +180,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { } private boolean tryApplyConstructor(InvocationExpr expr) { - if (expr.getAsyncTarget() != null || !expr.getMethod().getName().equals("")) { + if (!expr.getMethod().getName().equals("")) { return false; } if (resultSequence == null || resultSequence.isEmpty()) { @@ -615,17 +615,21 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { resultStmt = statement; } @Override public void visit(MonitorEnterStatement statement) { + statement.getObjectRef().acceptVisitor(this); + statement.setObjectRef(resultExpr); resultStmt = statement; } @Override public void visit(MonitorExitStatement statement) { + statement.getObjectRef().acceptVisitor(this); + statement.setObjectRef(resultExpr); 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 bca649fd4..c0d240ebd 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java @@ -119,16 +119,14 @@ class RedundantLabelEliminator implements StatementVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { } @Override public void visit(MonitorEnterStatement statement) { - } @Override public void visit(MonitorExitStatement 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 218b8ea45..031ca7d2a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java @@ -113,16 +113,14 @@ class ReferenceCountingVisitor implements StatementVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { } @Override public void visit(MonitorEnterStatement statement) { - } @Override public void visit(MonitorExitStatement 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 92f95159d..78abe9d5a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -260,8 +260,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } private void renderRuntimeAliases() throws IOException { - String[] names = { "$rt_asyncResult", "$rt_asyncError", "$rt_continue", "$rt_guardAsync", "$rt_throw", - "$rt_compare", "$rt_nullCheck", "$rt_cls", "$rt_createArray", "$rt_isInstance" }; + String[] names = { "$rt_throw", "$rt_compare", "$rt_nullCheck", "$rt_cls", "$rt_createArray", + "$rt_isInstance" }; boolean first = true; for (String name : names) { if (!first) { @@ -698,20 +698,20 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } writer.append("function $save()").ws().append("{").indent().softNewLine(); - writer.append("var $").ws().append('=').ws().append("$rt_stack();").softNewLine(); + writer.append("$rt_nativeThread()"); for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { - writer.append("$.push(").append(variableName(i)).append(");"); + writer.append(".push(").append(variableName(i)).append(")"); } - writer.append("$.push($ptr)"); + writer.append(".push($ptr);"); writer.softNewLine(); writer.outdent().append("}").softNewLine(); writer.append("$ptr").ws().append('=').ws().append("0;").softNewLine(); - writer.append("if").ws().append("($rt_isRestoring())").ws().append("{").indent().softNewLine(); - writer.append("var $s").ws().append('=').ws().append("$rt_stack();").softNewLine(); - writer.append("var $ptr").ws().append('=').append("$s.pop();"); + writer.append("if").ws().append("($rt_resuming())").ws().append("{").indent().softNewLine(); + writer.append("var $T").ws().append('=').ws().append("$rt_nativeThread();").softNewLine(); + writer.append("$ptr").ws().append('=').ws().append("$T.pop();"); for (int i = variableCount - 1; i > ref.parameterCount(); --i) { - writer.append(variableName(i)).ws().append('=').ws().append("$.pop();"); + writer.append(variableName(i)).ws().append('=').ws().append("T.pop();"); } writer.softNewLine(); writer.outdent().append("}").softNewLine(); @@ -1681,15 +1681,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } writer.append(')'); exitPriority(); - if (expr.getAsyncTarget() != null) { - writer.append(';').softNewLine(); - writer.append("$ptr").ws().append("=").ws().append(expr.getAsyncTarget()).append(";") - .softNewLine(); - writer.append("if").ws().append("($rt_suspending())").ws().append("{").indent(); - writer.append("return $save();").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.append("break $main;").softNewLine(); - } if (lastCallSite != null) { if (virtual) { lastCallSite.setVirtualMethod(expr.getMethod()); @@ -1965,18 +1956,25 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } } + @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement 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); + gotoPart(statement.getPart()); + } catch (IOException ex){ + throw new RenderingException("IO error occured", ex); } } + private void gotoPart(int part) throws IOException { + writer.append("$ptr").ws().append("=").ws().append(part).append(";") + .softNewLine(); + writer.append("if").ws().append("($rt_suspending())").ws().append("{").indent().softNewLine(); + writer.append("return $save();").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("break $main;").softNewLine(); + } + @Override public void visit(MonitorEnterStatement statement) { try { @@ -1986,6 +1984,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.appendMethodBody(monitorEnterRef).append("("); statement.getObjectRef().acceptVisitor(this); writer.append(");").softNewLine(); + if (statement.getAsyncTarget() != null) { + gotoPart(statement.getAsyncTarget()); + } } else { MethodReference monitorEnterRef = new MethodReference( Object.class, "monitorEnterSync", Object.class, void.class); diff --git a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java index 2400494ab..604bb2f3c 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -550,20 +550,18 @@ class StatementGenerator implements InstructionVisitor { } else { invocationExpr = Expr.invokeStatic(insn.getMethod(), exprArgs); } - invocationExpr.setAsyncTarget(asyncTarget); - if (asyncTarget == null) { - if (insn.getReceiver() != null) { - assign(invocationExpr, insn.getReceiver()); - } else { - AssignmentStatement stmt = Statement.assign(null, invocationExpr); - stmt.setLocation(currentLocation); - statements.add(stmt); - } + if (insn.getReceiver() != null) { + assign(invocationExpr, insn.getReceiver()); } else { AssignmentStatement stmt = Statement.assign(null, invocationExpr); stmt.setLocation(currentLocation); statements.add(stmt); } + if (asyncTarget != null) { + GotoPartStatement gotoStmt = new GotoPartStatement(); + gotoStmt.setPart(asyncTarget); + statements.add(gotoStmt); + } } @Override 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 252b10b0b..6bc07c4f0 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/TryCatchFinder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/TryCatchFinder.java @@ -112,16 +112,14 @@ class TryCatchFinder implements StatementVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { + public void visit(GotoPartStatement statement) { } @Override public void visit(MonitorEnterStatement statement) { - } @Override public void visit(MonitorExitStatement 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 ddea6cf0c..82c00b82a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java @@ -226,10 +226,7 @@ class UnusedVariableEliminator implements ExprVisitor, StatementVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { - if (statement.getReceiver() != null) { - statement.setReceiver(renumber(statement.getReceiver())); - } + public void visit(GotoPartStatement statement) { } @Override diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/RestoreAsyncStatement.java b/teavm-core/src/main/java/org/teavm/javascript/ast/GotoPartStatement.java similarity index 78% rename from teavm-core/src/main/java/org/teavm/javascript/ast/RestoreAsyncStatement.java rename to teavm-core/src/main/java/org/teavm/javascript/ast/GotoPartStatement.java index 4f903fd9b..9ccdb7ca5 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/RestoreAsyncStatement.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/GotoPartStatement.java @@ -19,15 +19,15 @@ package org.teavm.javascript.ast; * * @author Alexey Andreev */ -public class RestoreAsyncStatement extends Statement { - private Integer receiver; +public class GotoPartStatement extends Statement { + private int part; - public Integer getReceiver() { - return receiver; + public int getPart() { + return part; } - public void setReceiver(Integer receiver) { - this.receiver = receiver; + public void setPart(int part) { + this.part = part; } @Override diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/InvocationExpr.java b/teavm-core/src/main/java/org/teavm/javascript/ast/InvocationExpr.java index 8ee249842..2d462bb85 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/InvocationExpr.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/InvocationExpr.java @@ -28,7 +28,6 @@ public class InvocationExpr extends Expr { private MethodReference method; private InvocationType type; private List arguments = new ArrayList<>(); - private Integer asyncTarget; public MethodReference getMethod() { return method; @@ -50,14 +49,6 @@ public class InvocationExpr extends Expr { return arguments; } - public Integer getAsyncTarget() { - return asyncTarget; - } - - public void setAsyncTarget(Integer asyncTarget) { - this.asyncTarget = asyncTarget; - } - @Override public void acceptVisitor(ExprVisitor visitor) { visitor.visit(this); @@ -72,7 +63,6 @@ public class InvocationExpr extends Expr { InvocationExpr copy = new InvocationExpr(); cache.put(this, copy); copy.setMethod(method); - copy.setAsyncTarget(asyncTarget); for (Expr arg : arguments) { copy.getArguments().add(arg.clone(cache)); } 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 402fe6d3c..1bb1cfccd 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 @@ -193,19 +193,16 @@ public class RenamingVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(RestoreAsyncStatement statement) { - if (statement.getReceiver() != null) { - statement.setReceiver(varNames[statement.getReceiver()]); - } + public void visit(GotoPartStatement statement) { } @Override public void visit(MonitorEnterStatement statement) { - + } @Override public void visit(MonitorExitStatement statement) { - + } } 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 86cbb8474..7194cdb4e 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 @@ -44,9 +44,9 @@ public interface StatementVisitor { void visit(TryCatchStatement statement); - void visit(RestoreAsyncStatement statement); - + void visit(GotoPartStatement statement); + void visit(MonitorEnterStatement statement); - + void visit(MonitorExitStatement statement); } 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 b817fd3a7..81d174be4 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 @@ -62,16 +62,12 @@ public class AsyncProgramSplitter { int last = 0; for (int i = 0; i < sourceBlock.getInstructions().size(); ++i) { Instruction insn = sourceBlock.getInstructions().get(i); - Integer receiver; if (insn instanceof InvokeInstruction) { InvokeInstruction invoke = (InvokeInstruction)insn; if (!asyncMethods.contains(findRealMethod(invoke.getMethod()))) { continue; } - receiver = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null; - } else if (insn instanceof MonitorEnterInstruction) { - receiver = null; - } else { + } else if (!(insn instanceof MonitorEnterInstruction)) { continue; } @@ -102,7 +98,6 @@ public class AsyncProgramSplitter { // Create a new part Program nextProgram = createStubCopy(program); Part part = new Part(); - part.input = receiver; part.program = nextProgram; int partId = parts.size(); parts.add(part); @@ -201,10 +196,6 @@ public class AsyncProgramSplitter { return parts.get(index).program; } - public Integer getInput(int index) { - return parts.get(index).input; - } - public int[] getBlockSuccessors(int index) { int[] result = parts.get(index).blockSuccessors; return Arrays.copyOf(result, result.length); @@ -212,7 +203,6 @@ public class AsyncProgramSplitter { private static class Part { Program program; - Integer input; int[] blockSuccessors; } 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 e3107f9c4..0c6fdf36c 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -303,7 +303,7 @@ public class TeaVMTool { return; } if (mainClass != null) { - writer.append("main = $rt_mainWrapper(main);\n"); + writer.append("main = $rt_mainStarter(main);\n"); } ProblemProvider problemProvider = vm.getProblemProvider(); if (problemProvider.getProblems().isEmpty()) { 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 ee02d37ed..f004f7b26 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -223,13 +223,16 @@ function $rt_init(cls, constructor, args) { return obj; } function $rt_throw(ex) { + throw $rt_exception(ex); +} +function $rt_exception(ex) { var err = ex.$jsException; if (!err) { var err = new Error("Java exception thrown"); err.$javaException = ex; ex.$jsException = err; } - throw err; + return err; } function $rt_createMultiArray(cls, dimensions) { var arrays = new Array($rt_primitiveArrayCount(dimensions)); @@ -403,71 +406,15 @@ function $rt_metadata(data) { } } } -function $rt_asyncResult(value) { - return function() { - return value; - } -} -function $rt_asyncError(e) { - return function() { - throw new TeaVMAsyncError(e); - } -} -function $rt_staticAsyncAdapter(f) { - return function() { - var result; - var args = Array.prototype.slice.apply(arguments); - var $return = args.pop(); - try { - result = f.apply(this, args); - } catch (e) { - return $return($rt_asyncError(e)); - } - return $return($rt_asyncResult(result)); - } -} -function $rt_asyncAdapter(f) { - return function() { - var result; - var args = Array.prototype.slice.apply(arguments); - var $return = args.pop(); - args.unshift(this); - try { - result = f.apply(null, args); - } catch (e) { - return $return($rt_asyncError(e)); - } - return $return($rt_asyncResult(result)); - } -} -function $rt_rootInvocationAdapter(f) { +function $rt_threadStarter(f) { return function() { var args = Array.prototype.slice.apply(arguments); - args.push(function(result) { - try { - result(); - } catch (e) { - var prefix = "Exception occured %s at %o"; - var hasWrappers = false; - while (e instanceof TeaVMAsyncError) { - console.error(prefix, e.message, e.stack); - e = e.cause; - prefix = "Caused by %s at %o"; - hasWrappers = true; - } - console.error(!hasWrappers ? prefix : "Root cause is %s at %o", e.message, e.stack); - } + $rt_startThread(function() { + f.apply(this, args); }); - f.apply(this, args); - var thread = $rt_getThread(); - if (thread.hasOwnProperty("postponed")) { - while (thread.postponed.length > 0) { - thread.postponed.shift()(); - } - } } } -function $rt_mainWrapper(f) { +function $rt_mainStarter(f) { return function(args) { if (!args) { args = []; @@ -476,7 +423,7 @@ function $rt_mainWrapper(f) { for (var i = 0; i < args.length; ++i) { javaArgs.data[i] = $rt_str(args[i]); } - $rt_rootInvocationAdapter(f)(javaArgs); + $rt_threadStarter(f)(javaArgs); }; } var $rt_stringPool_instance; @@ -489,66 +436,51 @@ function $rt_stringPool(strings) { function $rt_s(index) { return $rt_stringPool_instance[index]; } -var $rt_continueCounter = 0; -function $rt_continue(f) { - if ($rt_continueCounter++ == 5) { - $rt_continueCounter = 0; - return function() { - var self = this; - var args = arguments; - var thread = $rt_getThread(); - if (!thread.hasOwnProperty("postponed")) { - thread.postponed = []; - } - thread.postponed.push(function() { - f.apply(self, args); - }); - }; - } else { - return f; - } -} -function $rt_guardAsync(f, continuation) { - return function() { - try { - return f.apply(this, arguments); - } catch (e) { - return continuation($rt_asyncError(e)); - } - } -} function TeaVMThread(runner) { this.status = 3; this.stack = []; this.suspendCallback = null; this.runner = runner; + this.attribute = null; } -TeaVMThread.push = function(value) { +TeaVMThread.prototype.push = function(value) { this.stack.push[value]; + return this; } -TeaVMThread.isResuming = function() { +TeaVMThread.prototype.pop = function() { + return this.stack.pop(); +} +TeaVMThread.prototype.isResuming = function() { return this.status == 1; } -TeaVMThread.isSuspending = function() { +TeaVMThread.prototype.isSuspending = function() { return this.status == 2; } -TeaVMThread.suspend(callback) { +TeaVMThread.prototype.suspend = function(callback) { this.suspendCallback = callback; this.status = 1; } -TeaVMThread.start = function() { +TeaVMThread.prototype.start = function() { if (this.status != 3) { throw new Error("Thread already started"); } + if ($rt_currentNativeThread !== null) { + throw new Error("Another thread is running"); + } this.status = 0; this.run(); } -TeaVMThread.resume = function() { +TeaVMThread.prototype.resume = function() { + if ($rt_currentNativeThread !== null) { + throw new Error("Another thread is running"); + } this.status = 2; this.run(); } -TeaVMThread.run = function() { +TeaVMThread.prototype.run = function() { + $rt_currentNativeThread = this; this.runner(); + $rt_currentNativeThread = null; if (this.suspendCallback !== null) { var self = this; this.suspendCallback(function() { @@ -556,27 +488,22 @@ TeaVMThread.run = function() { }); } } -function $rt_nativeThread(thread) { - if (!thread.hasNativeProperty("$teavm_thread")) { - thread.$teavm_thread = new TeaVMThread(); - } -} function $rt_suspending() { - return $rt_nativeThread($rt_getThread()).isSuspending(); + return $rt_nativeThread().isSuspending(); } function $rt_resuming() { - return $rt_nativeThread($rt_getThread()).isResuming(); + return $rt_nativeThread().isResuming(); } - -function TeaVMAsyncError(cause) { - this.message = "Async error occured"; - this.cause = cause; - if (cause) { - this.$javaException = cause.$javaException; - } +function $rt_suspend(callback) { + return $rt_nativeThread().suspend(callback); +} +function $rt_startThread(runner) { + new TeaVMThread(runner).start(); +} +var $rt_currentNativeThread = null; +function $rt_nativeThread() { + return $rt_currentNativeThread; } -TeaVMAsyncError.prototype = new Error(); -TeaVMAsyncError.prototype.constructor = TeaVMAsyncError; function $dbg_repr(obj) { return obj.toString ? obj.toString() : ""; diff --git a/teavm-platform/src/main/java/org/teavm/platform/Platform.java b/teavm-platform/src/main/java/org/teavm/platform/Platform.java index 1c8e18b05..cbf6fe506 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/Platform.java +++ b/teavm-platform/src/main/java/org/teavm/platform/Platform.java @@ -73,9 +73,17 @@ public final class Platform { return ((PlatformHelper)JS.getGlobal()).nextId(); } + public static T newInstance(PlatformClass cls) { + prepareNewInstance(); + return newInstanceImpl(cls); + } + + @GeneratedBy(PlatformGenerator.class) + private static native void prepareNewInstance(); + @GeneratedBy(PlatformGenerator.class) @PluggableDependency(PlatformGenerator.class) - public static native T newInstance(PlatformClass cls); + private static native T newInstanceImpl(PlatformClass cls); @GeneratedBy(PlatformGenerator.class) @PluggableDependency(PlatformGenerator.class) diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java index c7e4e27bb..fb2c30487 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java @@ -38,19 +38,29 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { MethodReference asyncRef = getAsyncReference(methodRef); + writer.append("var thread").ws().append('=').ws().append("$rt_nativeThread();").softNewLine(); + writer.append("if").ws().append("(thread.isResuming())").ws().append("{").indent().softNewLine(); + writer.append("var result").ws().append("=").ws().append("thread.attribute;").softNewLine(); + writer.append("if").ws().append("(result instanceof Error)").ws().append("{").indent().softNewLine(); + writer.append("throw result;").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return result;").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("var callback").ws().append("=").ws().append("function()").ws().append("{};").softNewLine(); writer.append("callback.").appendMethod(completeMethod.getDescriptor()).ws().append("=").ws() .append("function(val)").ws().append("{").indent().softNewLine(); - writer.append("return ").append(context.getCompleteContinuation()).append("($rt_asyncResult(val));") - .softNewLine(); + writer.append("thread.attribute").ws().append('=').ws().append("val;").softNewLine(); + writer.append("thread.resume();").softNewLine(); writer.outdent().append("};").softNewLine(); writer.append("callback.").appendMethod(errorMethod.getDescriptor()).ws().append("=").ws() .append("function(e)").ws().append("{").indent().softNewLine(); - writer.append("return ").append(context.getCompleteContinuation()).append("($rt_asyncError(e));") - .softNewLine(); + writer.append("thread.attribute").ws().append('=').ws().append("$rt_exception(e);").softNewLine(); + writer.append("thread.resume();").softNewLine(); writer.outdent().append("};").softNewLine(); + writer.append("return thread.suspend(function()").ws().append("{").indent().softNewLine(); writer.append("try").ws().append("{").indent().softNewLine(); - writer.append("return ").appendMethodBody(asyncRef).append('('); + writer.appendMethodBody(asyncRef).append('('); ClassReader cls = context.getClassSource().get(methodRef.getClassName()); MethodReader method = cls.getMethod(methodRef.getDescriptor()); int start = method.hasModifier(ElementModifier.STATIC) ? 1 : 0; @@ -60,9 +70,10 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin { } writer.append("callback);").softNewLine(); writer.outdent().append("}").ws().append("catch($e)").ws().append("{").indent().softNewLine(); - writer.append("return ").append(context.getCompleteContinuation()).append("($rt_asyncError($e));") + writer.append("callback.").appendMethod(errorMethod.getDescriptor()).append("($rt_exception($e));") .softNewLine(); writer.outdent().append("}").softNewLine(); + writer.outdent().append("});").softNewLine(); } private MethodReference getAsyncReference(MethodReference methodRef) { diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/NewInstanceDependencySupport.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/NewInstanceDependencySupport.java index cdb10e6e9..1a5647b9e 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/NewInstanceDependencySupport.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/NewInstanceDependencySupport.java @@ -49,7 +49,7 @@ public class NewInstanceDependencySupport implements DependencyListener { @Override public void methodAchieved(final DependencyAgent agent, MethodDependency method, CallLocation location) { MethodReader reader = method.getMethod(); - if (reader.getOwnerName().equals(Platform.class.getName()) && reader.getName().equals("newInstance")) { + if (reader.getOwnerName().equals(Platform.class.getName()) && reader.getName().equals("newInstanceImpl")) { allClassesNode.connect(method.getResult()); final MethodReference methodRef = reader.getReference(); method.getResult().addConsumer(new DependencyConsumer() { diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java index 756545a3c..bc05a14f2 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java @@ -68,8 +68,11 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { - case "newInstance": - generateNewInstance(context, writer, methodRef); + case "newInstanceImpl": + generateNewInstance(writer); + break; + case "prepareNewInstance": + generatePrepareNewInstance(context, writer); break; case "lookupClass": generateLookup(context, writer); @@ -89,76 +92,37 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin } } - private void generateNewInstance(GeneratorContext context, SourceWriter writer, MethodReference methodRef) + private void generatePrepareNewInstance(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("var c").ws().append("=").ws().append("'$$constructor$$';").softNewLine(); - if (context.isAsync()) { - writer.append("function async(cls, init) {").indent().softNewLine(); - writer.append("return function($return) {").indent().softNewLine(); - writer.append("var r = new cls;").softNewLine(); - writer.append("init(r, $rt_guardAsync(function($restore) {").indent().softNewLine(); - writer.append("$restore();").softNewLine(); - writer.append("$return($rt_asyncResult(r))").softNewLine(); - writer.outdent().append("}));").softNewLine(); - writer.outdent().append("};").softNewLine(); - writer.outdent().append("}").softNewLine(); - - writer.append("function sync(cls, init) {").indent().softNewLine(); - writer.append("return function($return) {").indent().softNewLine(); - writer.append("var r = new cls;").softNewLine(); - writer.append("try {").indent().softNewLine(); - writer.append("init(r);").softNewLine(); - writer.append("$return($rt_asyncResult(r));").softNewLine(); - writer.outdent().append("} catch (e) {").indent().softNewLine(); - writer.append("$return($rt_asyncError(e));").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.outdent().append("};").softNewLine(); - writer.outdent().append("}").softNewLine(); - } for (String clsName : context.getClassSource().getClassNames()) { ClassReader cls = context.getClassSource().get(clsName); MethodReader method = cls.getMethod(new MethodDescriptor("", void.class)); if (method != null) { - writer.appendClass(clsName).append("[c]").ws().append("=").ws(); - if (!context.isAsync()) { - writer.append(writer.getNaming().getNameForInit(method.getReference())); - } else { - String function = context.isAsync(method.getReference()) ? "async" : "sync"; - String methodName = context.isAsync(method.getReference()) ? - writer.getNaming().getFullNameForAsync(method.getReference()) : - writer.getNaming().getFullNameFor(method.getReference()); - writer.append(function).append("(").appendClass(clsName).append(',').ws() - .append(methodName).append(")"); - } - writer.append(";").softNewLine(); + writer.appendClass(clsName).append("[c]").ws().append("=").ws() + .appendMethodBody(method.getReference()).append(")").append(";").softNewLine(); } } - String selfName = context.isAsync() ? writer.getNaming().getFullNameForAsync(methodRef) : - writer.getNaming().getFullNameFor(methodRef); - writer.append(selfName).ws().append("=").ws().append("function(cls"); - if (context.isAsync()) { - writer.append(',').ws().append("$return"); - } - writer.append(")").ws().append("{").softNewLine().indent(); - writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine(); - if (!context.isAsync()) { - writer.append("return null;").softNewLine(); - } else { - writer.append("return $return($rt_asyncResult(null));").softNewLine(); - } - writer.outdent().append("}").softNewLine(); - if (!context.isAsync()) { - writer.append("return cls[c]();").softNewLine(); - } else { - writer.append("return cls[c]($return);").softNewLine(); - } - writer.outdent().append("};").softNewLine(); + writer.appendMethodBody(Platform.class, "newInstance", PlatformClass.class, Object.class).ws().append('=').ws() + .appendMethodBody(Platform.class, "newInstanceImpl", PlatformClass.class, Object.class) + .append(";").softNewLine(); + } - writer.append("return ").append(selfName).append("(").append(context.getParameterName(1)); - if (context.isAsync()) { - writer.append(',').ws().append(context.getCompleteContinuation()); - } - writer.append(");").softNewLine(); + private void generateNewInstance(SourceWriter writer) throws IOException { + writer.append("if").ws().append("($rt_resuming())").ws().append("{").indent().softNewLine(); + writer.append("return $rt_nativeThread().pop();").softNewLine(); + writer.outdent().append("}").softNewLine(); + + writer.append("if").ws().append("(!cls.hasOwnProperty('$$constructor$$'))").ws().append("{") + .indent().softNewLine(); + writer.append("return null;").softNewLine(); + writer.outdent().append("}").softNewLine(); + + writer.append("var $r").ws().append('=').ws().append("cls.$$constructor$$();"); + writer.append("if").ws().append("($rt_suspending())").ws().append("{").indent().softNewLine(); + writer.append("return $rt_nativeThread().push($r);").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return $r;").softNewLine(); } private void generateLookup(GeneratorContext context, SourceWriter writer) throws IOException { @@ -189,18 +153,8 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin PlatformRunnable.class, void.class); String runnable = context.getParameterName(1); writer.append("return window.setTimeout(function()").ws().append("{").indent().softNewLine(); - boolean async = context.isAsyncFamily(launchRef); - String methodName = async ? writer.getNaming().getFullNameForAsync(launchRef) : - writer.getNaming().getFullNameFor(launchRef); - if (async) { - writer.append("$rt_rootInvocationAdapter("); - } - writer.append(methodName); - if (async) { - writer.append(")"); - } - writer.append("(").append(runnable).append(");") - .softNewLine(); + writer.append("$rt_threadStarter(").appendMethodBody(launchRef).append(")"); + writer.append("(").append(runnable).append(");").softNewLine(); writer.outdent().append("},").ws().append(timeout ? context.getParameterName(2) : "0") .append(");").softNewLine(); } diff --git a/teavm-samples/teavm-samples-async/pom.xml b/teavm-samples/teavm-samples-async/pom.xml index 99a38de06..125597036 100644 --- a/teavm-samples/teavm-samples-async/pom.xml +++ b/teavm-samples/teavm-samples-async/pom.xml @@ -65,10 +65,12 @@ ${project.build.directory}/generated/js/teavm org.teavm.samples.async.AsyncProgram SEPARATE - true + false true +