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 42a220077..8319ae223 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -24,6 +24,7 @@ import org.teavm.javascript.ni.InjectedBy; import org.teavm.javascript.ni.PreserveOriginalName; import org.teavm.model.*; import org.teavm.model.util.AsyncProgramSplitter; +import org.teavm.model.util.ProgramUtils; /** * @@ -45,10 +46,12 @@ public class Decompiler { private Map generators = new HashMap<>(); private Set methodsToPass = new HashSet<>(); private RegularMethodNodeCache regularMethodCache; + private Set asyncMethods; - public Decompiler(ClassHolderSource classSource, ClassLoader classLoader) { + public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set asyncMethods) { this.classSource = classSource; this.classLoader = classLoader; + this.asyncMethods = asyncMethods; } public RegularMethodNodeCache getRegularMethodCache() { @@ -146,6 +149,7 @@ public class Decompiler { if (method.getAnnotations().get(PreserveOriginalName.class.getName()) != null) { methodNode.setOriginalNamePreserved(true); } + clsNode.getAsyncMethods().add(decompileAsync(method)); } clsNode.getInterfaces().addAll(cls.getInterfaces()); clsNode.getModifiers().addAll(mapModifiers(cls.getModifiers())); @@ -159,7 +163,7 @@ public class Decompiler { public MethodNode decompileAsync(MethodHolder method) { return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method, true) : - decompileAsync(method); + decompileRegularAsync(method); } public NativeMethodNode decompileNative(MethodHolder method, boolean async) { @@ -189,7 +193,6 @@ public class Decompiler { } public RegularMethodNode decompileRegular(MethodHolder method) { - // TODO: add caching in case of incremental build if (regularMethodCache == null) { return decompileRegularCacheMiss(method); } @@ -198,24 +201,18 @@ public class Decompiler { node = decompileRegularCacheMiss(method); regularMethodCache.store(method.getReference(), node); } - // TODO: add optimization - node.getModifiers().addAll(mapModifiers(method.getModifiers())); - int paramCount = Math.min(method.getSignature().length, method.getProgram().variableCount()); - for (int i = 0; i < paramCount; ++i) { - Variable var = method.getProgram().variableAt(i); - node.getParameterDebugNames().add(new HashSet<>(var.getDebugNames())); - } return node; } public AsyncMethodNode decompileRegularAsync(MethodHolder method) { AsyncMethodNode node = new AsyncMethodNode(method.getReference()); - AsyncProgramSplitter splitter = new AsyncProgramSplitter(); + AsyncProgramSplitter splitter = new AsyncProgramSplitter(asyncMethods); splitter.split(method.getProgram()); for (int i = 0; i < splitter.size(); ++i) { AsyncMethodPart part = new AsyncMethodPart(); part.setInputVariable(splitter.getInput(i)); - part.setStatement(getRegularMethodStatement(splitter.getProgram(i))); + part.setStatement(getRegularMethodStatement(splitter.getProgram(i), i, splitter.getBlockSuccessors(i))); + node.getBody().add(part); } return node; } @@ -223,7 +220,7 @@ public class Decompiler { public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) { RegularMethodNode methodNode = new RegularMethodNode(method.getReference()); Program program = method.getProgram(); - methodNode.setBody(getRegularMethodStatement(program)); + methodNode.setBody(getRegularMethodStatement(program, 0, new int[program.basicBlockCount()])); for (int i = 0; i < program.variableCount(); ++i) { methodNode.getVariables().add(program.variableAt(i).getRegister()); } @@ -238,8 +235,9 @@ public class Decompiler { return methodNode; } - private Statement getRegularMethodStatement(Program program) { + private Statement getRegularMethodStatement(Program program, int currentPart, int[] targetBlocks) { lastBlockId = 1; + graph = ProgramUtils.buildControlFlowGraph(program); indexer = new GraphIndexer(graph); graph = indexer.getGraph(); loopGraph = new LoopGraph(this.graph); @@ -289,9 +287,12 @@ public class Decompiler { int tmp = indexer.nodeAt(next); generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null; generator.statements.clear(); + generator.asyncTarget = null; InstructionLocation lastLocation = null; NodeLocation nodeLocation = null; - for (Instruction insn : generator.currentBlock.getInstructions()) { + List instructions = generator.currentBlock.getInstructions(); + for (int j = 0; j < instructions.size(); ++j) { + Instruction insn = generator.currentBlock.getInstructions().get(j); if (insn.getLocation() != null && lastLocation != insn.getLocation()) { lastLocation = insn.getLocation(); nodeLocation = new NodeLocation(lastLocation.getFileName(), lastLocation.getLine()); @@ -299,6 +300,9 @@ public class Decompiler { if (insn.getLocation() != null) { generator.setCurrentLocation(nodeLocation); } + if (targetBlocks[i] != currentPart && j == instructions.size() - 1) { + generator.asyncTarget = targetBlocks[i]; + } insn.acceptVisitor(generator); } for (TryCatchBlock tryCatch : generator.currentBlock.getTryCatchBlocks()) { 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 88528eaaf..393c63733 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -370,6 +370,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext for (MethodNode method : nonInitMethods) { renderBody(method, false); } + for (MethodNode method : cls.getAsyncMethods()) { + writer.append("/*").softNewLine(); + renderBody(method, false); + writer.append("*/").softNewLine(); + } renderVirtualDeclarations(cls.getName(), virtualMethods); } catch (NamingException e) { throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e); 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 4b69c3e52..9c3388ff6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -37,6 +37,7 @@ class StatementGenerator implements InstructionVisitor { Program program; ClassHolderSource classSource; private NodeLocation currentLocation; + Integer asyncTarget; public void setCurrentLocation(NodeLocation currentLocation) { this.currentLocation = currentLocation; @@ -546,7 +547,7 @@ class StatementGenerator implements InstructionVisitor { for (int i = 0; i < insn.getArguments().size(); ++i) { exprArgs[i] = Expr.var(insn.getArguments().get(i).getIndex()); } - Expr invocationExpr; + InvocationExpr invocationExpr; if (insn.getInstance() != null) { if (insn.getType() == InvocationType.VIRTUAL) { invocationExpr = Expr.invoke(insn.getMethod(), Expr.var(insn.getInstance().getIndex()), exprArgs); @@ -557,8 +558,15 @@ class StatementGenerator implements InstructionVisitor { } else { invocationExpr = Expr.invokeStatic(insn.getMethod(), exprArgs); } - if (insn.getReceiver() != null) { - assign(invocationExpr, insn.getReceiver()); + 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); + } } else { AssignmentStatement stmt = Statement.assign(null, invocationExpr); stmt.setLocation(currentLocation); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java index dcfcb3923..b76128af7 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java @@ -30,6 +30,7 @@ public class ClassNode { private Set modifiers = EnumSet.noneOf(NodeModifier.class); private List fields = new ArrayList<>(); private List methods = new ArrayList<>(); + private List asyncMethods = new ArrayList<>(); private List interfaces = new ArrayList<>(); public ClassNode(String name, String parentName) { @@ -53,6 +54,10 @@ public class ClassNode { return methods; } + public List getAsyncMethods() { + return asyncMethods; + } + public List getInterfaces() { return interfaces; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java b/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java index 9be6e91d1..476f79e6b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/Expr.java @@ -119,7 +119,7 @@ public abstract class Expr implements Cloneable { return expr; } - public static Expr invoke(MethodReference method, Expr target, Expr[] arguments) { + public static InvocationExpr invoke(MethodReference method, Expr target, Expr[] arguments) { InvocationExpr expr = new InvocationExpr(); expr.setMethod(method); expr.setType(InvocationType.DYNAMIC); @@ -128,7 +128,7 @@ public abstract class Expr implements Cloneable { return expr; } - public static Expr invokeSpecial(MethodReference method, Expr target, Expr[] arguments) { + public static InvocationExpr invokeSpecial(MethodReference method, Expr target, Expr[] arguments) { InvocationExpr expr = new InvocationExpr(); expr.setMethod(method); expr.setType(InvocationType.SPECIAL); @@ -137,7 +137,7 @@ public abstract class Expr implements Cloneable { return expr; } - public static Expr invokeStatic(MethodReference method, Expr[] arguments) { + public static InvocationExpr invokeStatic(MethodReference method, Expr[] arguments) { InvocationExpr expr = new InvocationExpr(); expr.setMethod(method); expr.setType(InvocationType.STATIC); 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 0ac79b9a7..0934da48b 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 @@ -15,11 +15,10 @@ */ package org.teavm.model.util; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.teavm.model.Program; +import java.util.*; +import org.teavm.model.*; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.JumpInstruction; /** * @@ -28,13 +27,116 @@ import org.teavm.model.Program; public class AsyncProgramSplitter { private List parts = new ArrayList<>(); private Map partMap = new HashMap<>(); + private Set asyncMethods = new HashSet<>(); + + public AsyncProgramSplitter(Set asyncMethods) { + this.asyncMethods = asyncMethods; + } public void split(Program program) { parts.clear(); - // TODO: implement splitting algorithm + Program initialProgram = createStubCopy(program); + Part initialPart = new Part(); + initialPart.program = initialProgram; + initialPart.blockSuccessors = new int[program.basicBlockCount()]; + parts.add(initialPart); + partMap.put(0L, 0); + Step initialStep = new Step(); + initialStep.source = 0; + initialStep.targetPart = initialPart; + Queue queue = new ArrayDeque<>(); + queue.add(initialStep); + + while (!queue.isEmpty()) { + Step step = queue.remove(); + BasicBlock targetBlock = step.targetPart.program.basicBlockAt(step.source); + if (targetBlock.instructionCount() > 0) { + continue; + } + BasicBlock sourceBlock = program.basicBlockAt(step.source); + int end = step.sourceIndex; + boolean asyncOccured = false; + for (int i = step.sourceIndex; i < sourceBlock.getInstructions().size(); ++i) { + Instruction insn = sourceBlock.getInstructions().get(i); + if (insn instanceof InvokeInstruction) { + InvokeInstruction invoke = (InvokeInstruction)insn; + if (true) { //asyncMethods.contains(invoke)) { + asyncOccured = true; + long key = ((long)step.source << 32) | i; + if (partMap.containsKey(key)) { + step.targetPart.blockSuccessors[step.sourceIndex] = partMap.get(key); + break; + } + Program nextProgram = createStubCopy(program); + BasicBlock nextBlock = nextProgram.basicBlockAt(step.source); + if (step.source > 0) { + JumpInstruction jumpToNextBlock = new JumpInstruction(); + jumpToNextBlock.setTarget(nextBlock); + nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock); + } + Part part = new Part(); + part.input = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null; + part.program = nextProgram; + int partId = parts.size(); + part.blockSuccessors = new int[program.basicBlockCount()]; + Arrays.fill(part.blockSuccessors, partId); + partMap.put(key, partId); + step.targetPart.blockSuccessors[step.source] = partId; + parts.add(part); + Step next = new Step(); + next.source = step.source; + next.sourceIndex = i + 1; + next.targetPart = part; + queue.add(next); + break; + } + } + } + targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, step.sourceIndex, end + 1, + targetBlock.getProgram())); + if (step.sourceIndex == 0) { + targetBlock.getPhis().addAll(ProgramUtils.copyPhis(sourceBlock, 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.sourceIndex = 0; + next.targetPart = step.targetPart; + queue.add(next); + } + } + if (!asyncOccured) { + InstructionTransitionExtractor successorExtractor = new InstructionTransitionExtractor(); + sourceBlock.getLastInstruction().acceptVisitor(successorExtractor); + for (BasicBlock successor : successorExtractor.getTargets()) { + BasicBlock targetSuccessor = targetBlock.getProgram().basicBlockAt(successor.getIndex()); + if (targetSuccessor.instructionCount() == 0) { + Step next = new Step(); + next.source = successor.getIndex(); + next.sourceIndex = 0; + next.targetPart = step.targetPart; + queue.add(next); + } + } + } + } + partMap.clear(); } + private Program createStubCopy(Program program) { + Program copy = new Program(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + copy.createBasicBlock(); + } + for (int i = 0; i < program.variableCount(); ++i) { + copy.createVariable(); + } + return copy; + } + public int size() { return parts.size(); } @@ -47,8 +149,20 @@ public class AsyncProgramSplitter { return parts.get(index).input; } + public int[] getBlockSuccessors(int index) { + int[] result = parts.get(index).blockSuccessors; + return Arrays.copyOf(result, result.length); + } + private static class Part { Program program; Integer input; + int[] blockSuccessors; + } + + private static class Step { + Part targetPart; + int source; + int sourceIndex; } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java index e3ca78d8e..a570efe8b 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -92,21 +92,8 @@ public final class ProgramUtils { for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlockReader block = program.basicBlockAt(i); BasicBlock blockCopy = copy.basicBlockAt(i); - for (int j = 0; j < block.instructionCount(); ++j) { - block.readInstruction(j, insnCopier); - blockCopy.getInstructions().add(insnCopier.copy); - } - for (PhiReader phi : block.readPhis()) { - Phi phiCopy = new Phi(); - phiCopy.setReceiver(copy.variableAt(phi.getReceiver().getIndex())); - for (IncomingReader incoming : phi.readIncomings()) { - Incoming incomingCopy = new Incoming(); - incomingCopy.setSource(copy.basicBlockAt(incoming.getSource().getIndex())); - incomingCopy.setValue(copy.variableAt(incoming.getValue().getIndex())); - phiCopy.getIncomings().add(incomingCopy); - } - blockCopy.getPhis().add(phiCopy); - } + blockCopy.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), copy)); + blockCopy.getPhis().addAll(copyPhis(block, copy)); for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) { TryCatchBlock tryCatchCopy = new TryCatchBlock(); tryCatchCopy.setExceptionType(tryCatch.getExceptionType()); @@ -118,6 +105,46 @@ public final class ProgramUtils { return copy; } + public static List copyInstructions(BasicBlockReader block, int from, int to, Program target) { + List result = new ArrayList<>(); + InstructionCopyReader copyReader = new InstructionCopyReader(); + copyReader.programCopy = target; + for (int i = from; i < to; ++i) { + block.readInstruction(i, copyReader); + copyReader.copy.setLocation(copyReader.location); + result.add(copyReader.copy); + } + return result; + } + + public static List copyPhis(BasicBlockReader block, Program target) { + List result = new ArrayList<>(); + for (PhiReader phi : block.readPhis()) { + Phi phiCopy = new Phi(); + phiCopy.setReceiver(target.variableAt(phi.getReceiver().getIndex())); + for (IncomingReader incoming : phi.readIncomings()) { + Incoming incomingCopy = new Incoming(); + incomingCopy.setSource(target.basicBlockAt(incoming.getSource().getIndex())); + incomingCopy.setValue(target.variableAt(incoming.getValue().getIndex())); + phiCopy.getIncomings().add(incomingCopy); + } + result.add(phiCopy); + } + return result; + } + + public static List copyTryCatches(BasicBlockReader block, Program target) { + List result = new ArrayList<>(); + for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) { + TryCatchBlock tryCatchCopy = new TryCatchBlock(); + tryCatchCopy.setExceptionType(tryCatch.getExceptionType()); + tryCatchCopy.setExceptionVariable(target.variableAt(tryCatch.getExceptionVariable().getIndex())); + tryCatchCopy.setHandler(target.basicBlockAt(tryCatch.getHandler().getIndex())); + result.add(tryCatchCopy); + } + return result; + } + private static class InstructionCopyReader implements InstructionReader { Instruction copy; Program programCopy; 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 27d261cd0..f7e43e5af 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -527,7 +527,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private List modelToAst(ListableClassHolderSource classes) { progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size()); - Decompiler decompiler = new Decompiler(classes, classLoader); + Decompiler decompiler = new Decompiler(classes, classLoader, new HashSet()); decompiler.setRegularMethodCache(incremental ? astCache : null); for (Map.Entry entry : methodGenerators.entrySet()) {