From b36c10760c361a0b7e87586e365a3bc293b46d47 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 2 Feb 2015 18:58:44 +0400 Subject: [PATCH] Working on async exception catching --- .../teavm/javascript/AsyncInvocationType.java | 25 +++++++++ .../java/org/teavm/javascript/Decompiler.java | 55 ++++++++++++++----- .../java/org/teavm/javascript/Renderer.java | 6 +- .../javascript/ast/AsyncMethodCatch.java | 49 +++++++++++++++++ .../teavm/javascript/ast/AsyncMethodPart.java | 8 +++ .../javascript/ast/TryCatchStatement.java | 9 +++ .../model/util/AsyncProgramSplitter.java | 8 +++ 7 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/javascript/AsyncInvocationType.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodCatch.java diff --git a/teavm-core/src/main/java/org/teavm/javascript/AsyncInvocationType.java b/teavm-core/src/main/java/org/teavm/javascript/AsyncInvocationType.java new file mode 100644 index 000000000..8e2c55563 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/AsyncInvocationType.java @@ -0,0 +1,25 @@ +/* + * 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.javascript; + +/** + * + * @author Alexey Andreev + */ +public enum AsyncInvocationType { + COMPLETE, + ERROR +} 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 8e97a19d9..c5f4ca06a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -203,9 +203,8 @@ public class Decompiler { AsyncProgramSplitter splitter = new AsyncProgramSplitter(asyncMethods); splitter.split(method.getProgram()); for (int i = 0; i < splitter.size(); ++i) { - AsyncMethodPart part = new AsyncMethodPart(); + AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i)); part.setInputVariable(splitter.getInput(i)); - part.setStatement(getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i))); node.getBody().add(part); } Program program = method.getProgram(); @@ -226,7 +225,9 @@ public class Decompiler { public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) { RegularMethodNode methodNode = new RegularMethodNode(method.getReference()); Program program = method.getProgram(); - methodNode.setBody(getRegularMethodStatement(program, new int[program.basicBlockCount()])); + int[] targetBlocks = new int[program.basicBlockCount()]; + Arrays.fill(targetBlocks, -1); + methodNode.setBody(getRegularMethodStatement(program, targetBlocks).getStatement()); for (int i = 0; i < program.variableCount(); ++i) { methodNode.getVariables().add(program.variableAt(i).getRegister()); } @@ -241,7 +242,8 @@ public class Decompiler { return methodNode; } - private Statement getRegularMethodStatement(Program program, int[] targetBlocks) { + private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks) { + AsyncMethodPart result = new AsyncMethodPart(); lastBlockId = 1; graph = ProgramUtils.buildControlFlowGraph(program); indexer = new GraphIndexer(graph); @@ -297,6 +299,7 @@ public class Decompiler { 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()) { @@ -308,26 +311,48 @@ 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()) { - 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); + 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); + generator.statements.clear(); + generator.statements.add(guardTryCatch); + } block.body.addAll(generator.statements); } } - SequentialStatement result = new SequentialStatement(); - result.getSequence().addAll(rootStmt.getBody()); + SequentialStatement resultBody = new SequentialStatement(); + resultBody.getSequence().addAll(rootStmt.getBody()); + result.setStatement(resultBody); return result; } 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 d6b55904d..e2ae3b11b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -939,11 +939,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext if (statement.getLocation() != null) { pushLocation(statement.getLocation()); } - if (!async) { - writer.append("$rt_throw("); - } else { - writer.append("return $throw("); - } + writer.append("$rt_throw("); prevCallSite = debugEmitter.emitCallSite(); statement.getException().acceptVisitor(this); writer.append(");").softNewLine(); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodCatch.java b/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodCatch.java new file mode 100644 index 000000000..09d79fa25 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/AsyncMethodCatch.java @@ -0,0 +1,49 @@ +/* + * 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.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 List getHandler() { + return handler; + } + + public String getExceptionType() { + return exceptionType; + } + + public void setExceptionType(String exceptionType) { + this.exceptionType = exceptionType; + } + + public Integer getExceptionVariable() { + return exceptionVariable; + } + + public void setExceptionVariable(Integer exceptionVariable) { + this.exceptionVariable = exceptionVariable; + } +} 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 aed946ea0..85f216951 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,6 +15,9 @@ */ package org.teavm.javascript.ast; +import java.util.ArrayList; +import java.util.List; + /** * * @author Alexey Andreev @@ -22,6 +25,7 @@ package org.teavm.javascript.ast; public class AsyncMethodPart { private Statement statement; private Integer inputVariable; + private List catches = new ArrayList<>(); public Statement getStatement() { return statement; @@ -38,4 +42,8 @@ public class AsyncMethodPart { 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/TryCatchStatement.java b/teavm-core/src/main/java/org/teavm/javascript/ast/TryCatchStatement.java index a5d11e66a..863cea302 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/TryCatchStatement.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/TryCatchStatement.java @@ -27,6 +27,7 @@ public class TryCatchStatement extends Statement { private List handler = new ArrayList<>(); private String exceptionType; private Integer exceptionVariable; + private boolean async; public List getProtectedBody() { return protectedBody; @@ -52,6 +53,14 @@ public class TryCatchStatement extends Statement { this.exceptionVariable = exceptionVariable; } + public boolean isAsync() { + return async; + } + + public void setAsync(boolean async) { + this.async = async; + } + @Override public void acceptVisitor(StatementVisitor visitor) { visitor.visit(this); 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 41628de0a..602a58c15 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 @@ -69,6 +69,14 @@ public class AsyncProgramSplitter { targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, last, i + 1, targetBlock.getProgram())); ProgramUtils.copyTryCatches(sourceBlock, targetBlock.getProgram()); + for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) { + if (tryCatch.getHandler() != null) { + Step next = new Step(); + next.source = tryCatch.getHandler().getIndex(); + next.targetPart = step.targetPart; + queue.add(next); + } + } last = i + 1; // If this instruction already separates program, end with current block and refer to the