From 1f5c5ce85e26e5b80120fa224e9ab82972306789 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 11 Apr 2015 23:18:38 +0300 Subject: [PATCH 01/16] Optimizing try/catch generator --- .../java/org/teavm/javascript/Decompiler.java | 143 +++++++++++++++--- 1 file changed, 121 insertions(+), 22 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 b0e285331..ee4752d02 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -47,6 +47,9 @@ public class Decompiler { private MethodNodeCache regularMethodCache; private Set asyncMethods; private Set splitMethods = new HashSet<>(); + private List tryCatchBookmarks = new ArrayList<>(); + private Deque stack; + private Program program; public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set asyncMethods, Set asyncFamilyMethods) { @@ -70,10 +73,12 @@ public class Decompiler { } static class Block { + public Block parent; public final IdentifiedStatement statement; public final List body; public final int end; public final int start; + public final List tryCatches = new ArrayList<>(); public Block(IdentifiedStatement statement, List body, int start, int end) { this.statement = statement; @@ -83,6 +88,14 @@ public class Decompiler { } } + static class TryCatchBookmark { + Block block; + int offset; + String exceptionType; + Integer exceptionVariable; + int exceptionHandler; + } + public List decompile(Collection classNames) { List sequence = new ArrayList<>(); Set visited = new HashSet<>(); @@ -286,7 +299,8 @@ public class Decompiler { loopGraph = new LoopGraph(this.graph); unflatCode(); blockMap = new Block[program.basicBlockCount() * 2 + 1]; - Deque stack = new ArrayDeque<>(); + stack = new ArrayDeque<>(); + this.program = program; BlockStatement rootStmt = new BlockStatement(); rootStmt.setId("root"); stack.push(new Block(rootStmt, rootStmt.getBody(), -1, -1)); @@ -299,6 +313,20 @@ public class Decompiler { currentNode = parentNode.getFirstChild(); generator.async = async; for (int i = 0; i < this.graph.size(); ++i) { + int node = i < indexer.size() ? indexer.nodeAt(i) : -1; + int next = i + 1; + int head = loops[i]; + if (head != -1 && loopSuccessors[head] == next) { + next = head; + } + + if (node >= 0) { + generator.currentBlock = program.basicBlockAt(node); + int tmp = indexer.nodeAt(next); + generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null; + } + + List inheritedBookmarks = new ArrayList<>(); Block block = stack.peek(); while (block.end == i) { Block oldBlock = block; @@ -310,26 +338,42 @@ public class Decompiler { blockMap[mappedStart] = block; } } + + for (int j = oldBlock.tryCatches.size() - 1; j >= 0; --j) { + TryCatchBookmark bookmark = oldBlock.tryCatches.get(j); + TryCatchStatement tryCatchStmt = new TryCatchStatement(); + tryCatchStmt.setExceptionType(bookmark.exceptionType); + tryCatchStmt.setExceptionVariable(tryCatchStmt.getExceptionVariable()); + tryCatchStmt.getHandler().add(generator.generateJumpStatement( + program.basicBlockAt(bookmark.exceptionHandler))); + List blockPart = oldBlock.body.subList(bookmark.offset, oldBlock.body.size()); + tryCatchStmt.getProtectedBody().addAll(blockPart); + blockPart.clear(); + blockPart.add(tryCatchStmt); + inheritedBookmarks.add(bookmark); + } + oldBlock.tryCatches.clear(); } + + for (int j = inheritedBookmarks.size() - 1; j >= 0; --j) { + TryCatchBookmark bookmark = inheritedBookmarks.get(j); + bookmark.block = block; + bookmark.offset = block.body.size(); + block.tryCatches.add(bookmark); + } + while (parentNode.getEnd() == i) { currentNode = parentNode.getNext(); parentNode = parentNode.getParent(); } for (Block newBlock : createBlocks(i)) { block.body.add(newBlock.statement); + newBlock.parent = block; stack.push(newBlock); block = newBlock; } - int node = i < indexer.size() ? indexer.nodeAt(i) : -1; - int next = i + 1; - int head = loops[i]; - if (head != -1 && loopSuccessors[head] == next) { - next = head; - } + if (node >= 0) { - generator.currentBlock = program.basicBlockAt(node); - int tmp = indexer.nodeAt(next); - generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null; generator.statements.clear(); InstructionLocation lastLocation = null; NodeLocation nodeLocation = null; @@ -351,18 +395,7 @@ public class Decompiler { generator.statements.add(stmt); } - 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); - } - } + updateTryCatchBookmarks(generator, generator.currentBlock.getTryCatchBlocks()); block.body.addAll(generator.statements); } } @@ -372,6 +405,72 @@ public class Decompiler { return result; } + private void updateTryCatchBookmarks(StatementGenerator generator, List tryCatchBlocks) { + tryCatchBlocks = new ArrayList<>(tryCatchBlocks); + Collections.reverse(tryCatchBlocks); + + // Find which try catch blocks have remained since the previous basic block + int sz = Math.min(tryCatchBlocks.size(), tryCatchBookmarks.size()); + int start; + for (start = 0; start < sz; ++start) { + TryCatchBlock tryCatch = tryCatchBlocks.get(start); + TryCatchBookmark bookmark = tryCatchBookmarks.get(start); + if (tryCatch.getHandler().getIndex() != bookmark.exceptionHandler) { + break; + } + if (!Objects.equals(tryCatch.getExceptionType(), bookmark.exceptionType)) { + break; + } + if (tryCatch.getExceptionVariable() != null && bookmark.exceptionVariable != null && + tryCatch.getExceptionVariable().getIndex() != bookmark.exceptionVariable.intValue()) { + break; + } + } + + // Close old bookmarks + for (int i = tryCatchBookmarks.size() - 1; i >= start; --i) { + TryCatchBookmark bookmark = tryCatchBookmarks.get(i); + Block block = stack.peek(); + while (block != bookmark.block) { + TryCatchStatement tryCatchStmt = new TryCatchStatement(); + tryCatchStmt.setExceptionType(bookmark.exceptionType); + tryCatchStmt.setExceptionVariable(tryCatchStmt.getExceptionVariable()); + tryCatchStmt.getHandler().add(generator.generateJumpStatement( + program.basicBlockAt(bookmark.exceptionHandler))); + tryCatchStmt.getProtectedBody().addAll(block.body); + block.body.clear(); + block.body.add(tryCatchStmt); + block = block.parent; + } + + TryCatchStatement tryCatchStmt = new TryCatchStatement(); + tryCatchStmt.setExceptionType(bookmark.exceptionType); + tryCatchStmt.setExceptionVariable(tryCatchStmt.getExceptionVariable()); + tryCatchStmt.getHandler().add(generator.generateJumpStatement( + program.basicBlockAt(bookmark.exceptionHandler))); + List blockPart = block.body.subList(bookmark.offset, block.body.size()); + tryCatchStmt.getProtectedBody().addAll(blockPart); + blockPart.clear(); + blockPart.add(tryCatchStmt); + + bookmark.block.tryCatches.remove(bookmark); + } + + // Add new bookmarks + for (int i = start; i < tryCatchBlocks.size(); ++i) { + TryCatchBlock tryCatch = tryCatchBlocks.get(i); + TryCatchBookmark bookmark = new TryCatchBookmark(); + bookmark.block = stack.peek(); + bookmark.offset = bookmark.block.body.size(); + bookmark.exceptionHandler = tryCatch.getHandler().getIndex(); + bookmark.exceptionType = tryCatch.getExceptionType(); + bookmark.exceptionVariable = tryCatch.getExceptionVariable() != null ? + tryCatch.getExceptionVariable().getIndex() : null; + bookmark.block.tryCatches.add(bookmark); + tryCatchBookmarks.add(bookmark); + } + } + private Set mapModifiers(Set modifiers) { Set result = EnumSet.noneOf(NodeModifier.class); if (modifiers.contains(ElementModifier.STATIC)) { From 4b4af6dd4ef7f603e206ce376509f99a65553ae5 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 15 Apr 2015 20:18:05 +0300 Subject: [PATCH 02/16] Fix bugs in optimized try/catch decompiler --- .../src/main/java/org/teavm/cli/TeaVMRunner.java | 4 ++-- .../org/teavm/javascript/BreakEliminator.java | 2 +- .../java/org/teavm/javascript/Decompiler.java | 16 ++++++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java b/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java index 18e3daa67..9062cd5cf 100644 --- a/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java +++ b/teavm-cli/src/main/java/org/teavm/cli/TeaVMRunner.java @@ -93,13 +93,13 @@ public final class TeaVMRunner { .create('c')); options.addOption(OptionBuilder .withDescription("Wait for command after compilation, in order to enable hot recompilation") - .withLongOpt("--wait") + .withLongOpt("wait") .create('w')); options.addOption(OptionBuilder .withArgName("classpath") .hasArgs() .withDescription("Additional classpath that will be reloaded by TeaVM each time in wait mode") - .withLongOpt("--classpath") + .withLongOpt("classpath") .create('p')); if (args.length == 0) { diff --git a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java index b0421551a..ccf116da6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java @@ -143,9 +143,9 @@ class BreakEliminator implements StatementVisitor { outerStatements = new HashSet<>(); blockSuccessors = new HashMap<>(); processSequence(statement.getProtectedBody()); - processSequence(statement.getHandler()); outerStatements = oldOuterStatements; blockSuccessors = oldBlockSuccessors; + processSequence(statement.getHandler()); } @Override 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 ee4752d02..efba6b520 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -289,7 +289,7 @@ public class Decompiler { private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks, boolean async) { AsyncMethodPart result = new AsyncMethodPart(); lastBlockId = 1; - graph = ProgramUtils.buildControlFlowGraph(program); + graph = ProgramUtils.buildControlFlowGraphWithTryCatch(program); int[] weights = new int[graph.size()]; for (int i = 0; i < weights.length; ++i) { weights[i] = program.basicBlockAt(i).getInstructions().size(); @@ -349,7 +349,9 @@ public class Decompiler { List blockPart = oldBlock.body.subList(bookmark.offset, oldBlock.body.size()); tryCatchStmt.getProtectedBody().addAll(blockPart); blockPart.clear(); - blockPart.add(tryCatchStmt); + if (!tryCatchStmt.getProtectedBody().isEmpty()) { + blockPart.add(tryCatchStmt); + } inheritedBookmarks.add(bookmark); } oldBlock.tryCatches.clear(); @@ -439,7 +441,9 @@ public class Decompiler { program.basicBlockAt(bookmark.exceptionHandler))); tryCatchStmt.getProtectedBody().addAll(block.body); block.body.clear(); - block.body.add(tryCatchStmt); + if (!tryCatchStmt.getProtectedBody().isEmpty()) { + block.body.add(tryCatchStmt); + } block = block.parent; } @@ -451,11 +455,15 @@ public class Decompiler { List blockPart = block.body.subList(bookmark.offset, block.body.size()); tryCatchStmt.getProtectedBody().addAll(blockPart); blockPart.clear(); - blockPart.add(tryCatchStmt); + if (!tryCatchStmt.getProtectedBody().isEmpty()) { + blockPart.add(tryCatchStmt); + } bookmark.block.tryCatches.remove(bookmark); } + tryCatchBookmarks.subList(start, tryCatchBookmarks.size()).clear(); + // Add new bookmarks for (int i = start; i < tryCatchBlocks.size(); ++i) { TryCatchBlock tryCatch = tryCatchBlocks.get(i); From 9fac86da7ca1dbad4f27c179d3fbd6232588cc7d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 15 Apr 2015 22:34:08 +0300 Subject: [PATCH 03/16] Fix bugs in try/catch generator --- .../src/main/java/org/teavm/javascript/Decompiler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 efba6b520..c0b3e263b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -343,7 +343,7 @@ public class Decompiler { TryCatchBookmark bookmark = oldBlock.tryCatches.get(j); TryCatchStatement tryCatchStmt = new TryCatchStatement(); tryCatchStmt.setExceptionType(bookmark.exceptionType); - tryCatchStmt.setExceptionVariable(tryCatchStmt.getExceptionVariable()); + tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable); tryCatchStmt.getHandler().add(generator.generateJumpStatement( program.basicBlockAt(bookmark.exceptionHandler))); List blockPart = oldBlock.body.subList(bookmark.offset, oldBlock.body.size()); @@ -436,7 +436,7 @@ public class Decompiler { while (block != bookmark.block) { TryCatchStatement tryCatchStmt = new TryCatchStatement(); tryCatchStmt.setExceptionType(bookmark.exceptionType); - tryCatchStmt.setExceptionVariable(tryCatchStmt.getExceptionVariable()); + tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable); tryCatchStmt.getHandler().add(generator.generateJumpStatement( program.basicBlockAt(bookmark.exceptionHandler))); tryCatchStmt.getProtectedBody().addAll(block.body); @@ -449,7 +449,7 @@ public class Decompiler { TryCatchStatement tryCatchStmt = new TryCatchStatement(); tryCatchStmt.setExceptionType(bookmark.exceptionType); - tryCatchStmt.setExceptionVariable(tryCatchStmt.getExceptionVariable()); + tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable); tryCatchStmt.getHandler().add(generator.generateJumpStatement( program.basicBlockAt(bookmark.exceptionHandler))); List blockPart = block.body.subList(bookmark.offset, block.body.size()); From 45b3805a2d7417c92418f9a66e44bc1b8dd94a26 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 16 Apr 2015 22:15:39 +0300 Subject: [PATCH 04/16] Fix more bugs in try/catch generator --- .../java/org/teavm/javascript/Decompiler.java | 60 ++++++++++++++++--- .../model/util/AsyncProgramSplitter.java | 4 ++ 2 files changed, 57 insertions(+), 7 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 c0b3e263b..73e5569db 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -15,13 +15,56 @@ */ package org.teavm.javascript; -import java.util.*; -import org.teavm.common.*; -import org.teavm.javascript.ast.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.teavm.common.Graph; +import org.teavm.common.GraphIndexer; +import org.teavm.common.Loop; +import org.teavm.common.LoopGraph; +import org.teavm.common.RangeTree; +import org.teavm.javascript.ast.AsyncMethodNode; +import org.teavm.javascript.ast.AsyncMethodPart; +import org.teavm.javascript.ast.BlockStatement; +import org.teavm.javascript.ast.ClassNode; +import org.teavm.javascript.ast.FieldNode; +import org.teavm.javascript.ast.GotoPartStatement; +import org.teavm.javascript.ast.IdentifiedStatement; +import org.teavm.javascript.ast.MethodNode; +import org.teavm.javascript.ast.NativeMethodNode; +import org.teavm.javascript.ast.NodeLocation; +import org.teavm.javascript.ast.NodeModifier; +import org.teavm.javascript.ast.RegularMethodNode; +import org.teavm.javascript.ast.SequentialStatement; +import org.teavm.javascript.ast.Statement; +import org.teavm.javascript.ast.TryCatchStatement; +import org.teavm.javascript.ast.WhileStatement; import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.Generator; import org.teavm.javascript.spi.InjectedBy; -import org.teavm.model.*; +import org.teavm.model.AnnotationHolder; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; +import org.teavm.model.ElementModifier; +import org.teavm.model.FieldHolder; +import org.teavm.model.Instruction; +import org.teavm.model.InstructionLocation; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.TryCatchBlock; +import org.teavm.model.ValueType; +import org.teavm.model.Variable; import org.teavm.model.util.AsyncProgramSplitter; import org.teavm.model.util.ProgramUtils; @@ -74,6 +117,7 @@ public class Decompiler { static class Block { public Block parent; + public int parentOffset; public final IdentifiedStatement statement; public final List body; public final int end; @@ -371,10 +415,13 @@ public class Decompiler { for (Block newBlock : createBlocks(i)) { block.body.add(newBlock.statement); newBlock.parent = block; + newBlock.parentOffset = block.body.size(); stack.push(newBlock); block = newBlock; } + updateTryCatchBookmarks(generator, generator.currentBlock.getTryCatchBlocks()); + if (node >= 0) { generator.statements.clear(); InstructionLocation lastLocation = null; @@ -397,7 +444,6 @@ public class Decompiler { generator.statements.add(stmt); } - updateTryCatchBookmarks(generator, generator.currentBlock.getTryCatchBlocks()); block.body.addAll(generator.statements); } } @@ -424,7 +470,7 @@ public class Decompiler { break; } if (tryCatch.getExceptionVariable() != null && bookmark.exceptionVariable != null && - tryCatch.getExceptionVariable().getIndex() != bookmark.exceptionVariable.intValue()) { + tryCatch.getExceptionVariable().getRegister() != bookmark.exceptionVariable.intValue()) { break; } } @@ -473,7 +519,7 @@ public class Decompiler { bookmark.exceptionHandler = tryCatch.getHandler().getIndex(); bookmark.exceptionType = tryCatch.getExceptionType(); bookmark.exceptionVariable = tryCatch.getExceptionVariable() != null ? - tryCatch.getExceptionVariable().getIndex() : null; + tryCatch.getExceptionVariable().getRegister() : null; bookmark.block.tryCatches.add(bookmark); tryCatchBookmarks.add(bookmark); } 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 dbfa1ac58..dc0cd0a5d 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 @@ -189,7 +189,11 @@ public class AsyncProgramSplitter { copy.createBasicBlock(); } for (int i = 0; i < program.variableCount(); ++i) { + Variable var = program.variableAt(i); copy.createVariable(); + Variable varCopy = copy.variableAt(i); + varCopy.setRegister(var.getRegister()); + varCopy.getDebugNames().addAll(var.getDebugNames()); } return copy; } From b3f662327ca6eb329093a274823296b314190169 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 16 Apr 2015 23:09:32 +0300 Subject: [PATCH 05/16] Fix bug in try/catch generator --- .../main/java/org/teavm/javascript/Decompiler.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 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 73e5569db..f2f1f2ea6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -370,6 +370,8 @@ public class Decompiler { generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null; } + closeExpiredBookmarks(generator, generator.currentBlock.getTryCatchBlocks()); + List inheritedBookmarks = new ArrayList<>(); Block block = stack.peek(); while (block.end == i) { @@ -419,8 +421,7 @@ public class Decompiler { stack.push(newBlock); block = newBlock; } - - updateTryCatchBookmarks(generator, generator.currentBlock.getTryCatchBlocks()); + createNewBookmarks(generator.currentBlock.getTryCatchBlocks()); if (node >= 0) { generator.statements.clear(); @@ -453,7 +454,7 @@ public class Decompiler { return result; } - private void updateTryCatchBookmarks(StatementGenerator generator, List tryCatchBlocks) { + private void closeExpiredBookmarks(StatementGenerator generator, List tryCatchBlocks) { tryCatchBlocks = new ArrayList<>(tryCatchBlocks); Collections.reverse(tryCatchBlocks); @@ -510,8 +511,11 @@ public class Decompiler { tryCatchBookmarks.subList(start, tryCatchBookmarks.size()).clear(); + } + + private void createNewBookmarks(List tryCatchBlocks) { // Add new bookmarks - for (int i = start; i < tryCatchBlocks.size(); ++i) { + for (int i = tryCatchBookmarks.size(); i < tryCatchBlocks.size(); ++i) { TryCatchBlock tryCatch = tryCatchBlocks.get(i); TryCatchBookmark bookmark = new TryCatchBookmark(); bookmark.block = stack.peek(); From eb48c349d01b457a472017ae4e7f1a173d2d6538 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 17 Apr 2015 22:52:02 +0300 Subject: [PATCH 06/16] Fix issues in break elimination --- .../org/teavm/javascript/BreakEliminator.java | 2 +- .../javascript/EscapingStatementFinder.java | 51 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java index ccf116da6..7115f06e4 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java @@ -161,6 +161,6 @@ class BreakEliminator implements StatementVisitor { } private boolean escapes(List statements) { - return new EscapingStatementFinder(outerStatements).check(statements); + return new EscapingStatementFinder().check(statements); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java b/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java index 79017d0b3..8698cf20a 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java @@ -15,6 +15,7 @@ */ package org.teavm.javascript; +import java.util.HashSet; import java.util.List; import java.util.Set; import org.teavm.javascript.ast.*; @@ -23,28 +24,38 @@ import org.teavm.javascript.ast.*; * * @author Alexey Andreev */ -class EscapingStatementFinder implements StatementVisitor{ +class EscapingStatementFinder implements StatementVisitor { public boolean escaping; - private Set outerStatements; - - public EscapingStatementFinder(Set nestingStatements) { - this.outerStatements = nestingStatements; - } + private boolean last = true; + private Set outerStatements = new HashSet<>(); public boolean check(List statements) { - if (!escaping) { - if (statements.isEmpty()) { - escaping = true; - } else { - statements.get(statements.size() - 1).acceptVisitor(this); + if (escaping) { + return true; + } + if (statements.isEmpty()) { + escaping = last; + return true; + } + boolean oldLast = last; + for (int i = 0; i < statements.size(); ++i) { + last = false; + statements.get(i).acceptVisitor(this); + if (escaping) { + break; } } + last = oldLast; + if (!escaping) { + statements.get(statements.size() - 1).acceptVisitor(this); + } + last = oldLast; return escaping; } @Override public void visit(AssignmentStatement statement) { - escaping = true; + escaping |= last; } @Override @@ -73,7 +84,11 @@ class EscapingStatementFinder implements StatementVisitor{ @Override public void visit(WhileStatement statement) { - escaping = true; + outerStatements.add(statement); + if (!check(statement.getBody()) && statement.getCondition() != null) { + escaping |= last; + } + outerStatements.remove(statement); } @Override @@ -85,12 +100,12 @@ class EscapingStatementFinder implements StatementVisitor{ @Override public void visit(BreakStatement statement) { - escaping = !outerStatements.contains(statement.getTarget()); + escaping |= !outerStatements.contains(statement.getTarget()); } @Override public void visit(ContinueStatement statement) { - escaping = !outerStatements.contains(statement.getTarget()); + escaping |= !outerStatements.contains(statement.getTarget()); } @Override @@ -103,7 +118,7 @@ class EscapingStatementFinder implements StatementVisitor{ @Override public void visit(InitClassStatement statement) { - escaping = true; + escaping |= last; } @Override @@ -118,11 +133,11 @@ class EscapingStatementFinder implements StatementVisitor{ @Override public void visit(MonitorEnterStatement statement) { - escaping = true; + escaping |= last; } @Override public void visit(MonitorExitStatement statement) { - escaping = true; + escaping |= last; } } From b7c7c2da739fcc6fb01b4d0be9623719a882133d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 18 Apr 2015 08:37:22 +0300 Subject: [PATCH 07/16] Fix bug with exception catch variable --- .../src/main/java/org/teavm/javascript/Decompiler.java | 6 +----- 1 file changed, 1 insertion(+), 5 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 f2f1f2ea6..97e19d484 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -470,10 +470,6 @@ public class Decompiler { if (!Objects.equals(tryCatch.getExceptionType(), bookmark.exceptionType)) { break; } - if (tryCatch.getExceptionVariable() != null && bookmark.exceptionVariable != null && - tryCatch.getExceptionVariable().getRegister() != bookmark.exceptionVariable.intValue()) { - break; - } } // Close old bookmarks @@ -523,7 +519,7 @@ public class Decompiler { bookmark.exceptionHandler = tryCatch.getHandler().getIndex(); bookmark.exceptionType = tryCatch.getExceptionType(); bookmark.exceptionVariable = tryCatch.getExceptionVariable() != null ? - tryCatch.getExceptionVariable().getRegister() : null; + tryCatch.getExceptionVariable().getIndex() : null; bookmark.block.tryCatches.add(bookmark); tryCatchBookmarks.add(bookmark); } From 358766d5f6ad19e7e3040269141f483722001139 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 19 Apr 2015 15:33:56 +0300 Subject: [PATCH 08/16] Fix bugs --- .../javascript/AllBlocksCountVisitor.java | 46 +++++++++++++++++-- .../org/teavm/javascript/BreakEliminator.java | 7 +-- .../javascript/EscapingStatementFinder.java | 26 ++++++++++- 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/javascript/AllBlocksCountVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/AllBlocksCountVisitor.java index 7f0d0fd0e..6b4536197 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/AllBlocksCountVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/AllBlocksCountVisitor.java @@ -27,14 +27,24 @@ import org.teavm.javascript.ast.*; class AllBlocksCountVisitor implements StatementVisitor { private Map blocksCount = new HashMap<>(); private IdentifiedStatement currentBlock; + private boolean last = true; public void visit(List statements) { if (statements == null) { return; } - for (Statement part : statements) { - part.acceptVisitor(this); + if (statements.isEmpty()) { + incrementCurrentBlock(); + return; } + boolean oldLast = last; + for (int i = 0; i < statements.size() - 1; ++i) { + last = false; + statements.get(i).acceptVisitor(this); + } + last = true; + statements.get(statements.size() - 1).acceptVisitor(this); + last = oldLast; } public int getCount(IdentifiedStatement statement) { @@ -44,6 +54,9 @@ class AllBlocksCountVisitor implements StatementVisitor { @Override public void visit(AssignmentStatement statement) { + if (last) { + incrementCurrentBlock(); + } } @Override @@ -66,6 +79,9 @@ class AllBlocksCountVisitor implements StatementVisitor { } visit(statement.getDefaultClause()); currentBlock = oldCurrentBlock; + if (last && blocksCount.containsKey(statement)) { + incrementCurrentBlock(); + } } @Override @@ -74,6 +90,9 @@ class AllBlocksCountVisitor implements StatementVisitor { currentBlock = statement; visit(statement.getBody()); currentBlock = oldCurrentBlock; + if (last && (statement.getCondition() != null || blocksCount.containsKey(statement))) { + incrementCurrentBlock(); + } } @Override @@ -82,6 +101,9 @@ class AllBlocksCountVisitor implements StatementVisitor { currentBlock = statement; visit(statement.getBody()); currentBlock = oldCurrentBlock; + if (last && blocksCount.containsKey(statement)) { + incrementCurrentBlock(); + } } @Override @@ -90,7 +112,7 @@ class AllBlocksCountVisitor implements StatementVisitor { if (target == null) { target = currentBlock; } - blocksCount.put(target, getCount(target) + 1); + incrementBlock(target); } @Override @@ -99,7 +121,17 @@ class AllBlocksCountVisitor implements StatementVisitor { if (target == null) { target = currentBlock; } - blocksCount.put(target, getCount(target) + 1); + incrementBlock(target); + } + + private void incrementBlock(IdentifiedStatement statement) { + blocksCount.put(statement, getCount(statement) + 1); + } + + private void incrementCurrentBlock() { + if (currentBlock != null) { + incrementBlock(currentBlock); + } } @Override @@ -126,9 +158,15 @@ class AllBlocksCountVisitor implements StatementVisitor { @Override public void visit(MonitorEnterStatement statement) { + if (last) { + incrementCurrentBlock(); + } } @Override public void visit(MonitorExitStatement statement) { + if (last) { + incrementCurrentBlock(); + } } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java index 7115f06e4..bff4928d8 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java @@ -26,7 +26,6 @@ class BreakEliminator implements StatementVisitor { private Map> blockSuccessors = new HashMap<>(); private Set outerStatements = new HashSet<>(); private List currentSequence; - private boolean sequenceEscapes; private int currentIndex; private AllBlocksCountVisitor usageCounter; @@ -39,15 +38,12 @@ class BreakEliminator implements StatementVisitor { private void processSequence(List statements) { List oldSequence = currentSequence; int oldIndex = currentIndex; - boolean oldEscapes = sequenceEscapes; - sequenceEscapes = escapes(statements); currentSequence = statements; for (currentIndex = 0; currentIndex < currentSequence.size(); ++currentIndex) { statements.get(currentIndex).acceptVisitor(this); } - sequenceEscapes = oldEscapes; currentIndex = oldIndex; currentSequence = oldSequence; } @@ -93,7 +89,7 @@ class BreakEliminator implements StatementVisitor { @Override public void visit(BlockStatement statement) { outerStatements.add(statement); - if (!sequenceEscapes && !escapes(statement.getBody())) { + if (!escapes(currentSequence.subList(currentIndex + 1, currentSequence.size()))) { blockSuccessors.put(statement, currentSequence.subList(currentIndex + 1, currentSequence.size())); } processSequence(statement.getBody()); @@ -110,7 +106,6 @@ class BreakEliminator implements StatementVisitor { currentSequence.addAll(successors); successors.clear(); --currentIndex; - sequenceEscapes = escapes(currentSequence); return; } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java b/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java index 8698cf20a..921ac1142 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java @@ -28,6 +28,8 @@ class EscapingStatementFinder implements StatementVisitor { public boolean escaping; private boolean last = true; private Set outerStatements = new HashSet<>(); + private Set breakTargets = new HashSet<>(); + private IdentifiedStatement currentBlock; public boolean check(List statements) { if (escaping) { @@ -72,6 +74,8 @@ class EscapingStatementFinder implements StatementVisitor { @Override public void visit(SwitchStatement statement) { + IdentifiedStatement oldCurrentBlock = currentBlock; + currentBlock = statement; outerStatements.add(statement); for (SwitchClause clause : statement.getClauses()) { if (check(clause.getBody())) { @@ -80,32 +84,50 @@ class EscapingStatementFinder implements StatementVisitor { } check(statement.getDefaultClause()); outerStatements.remove(statement); + currentBlock = oldCurrentBlock; + if (breakTargets.contains(statement)) { + escaping |= last; + } } @Override public void visit(WhileStatement statement) { + IdentifiedStatement oldCurrentBlock = currentBlock; + currentBlock = statement; outerStatements.add(statement); if (!check(statement.getBody()) && statement.getCondition() != null) { escaping |= last; } outerStatements.remove(statement); + currentBlock = oldCurrentBlock; + if (breakTargets.contains(statement)) { + escaping |= last; + } } @Override public void visit(BlockStatement statement) { + IdentifiedStatement oldCurrentBlock = currentBlock; + currentBlock = statement; outerStatements.add(statement); check(statement.getBody()); outerStatements.remove(statement); + currentBlock = oldCurrentBlock; + if (breakTargets.contains(statement)) { + escaping |= last; + } } @Override public void visit(BreakStatement statement) { - escaping |= !outerStatements.contains(statement.getTarget()); + IdentifiedStatement target = statement.getTarget() != null ? statement.getTarget() : currentBlock; + if (target != null) { + breakTargets.add(target); + } } @Override public void visit(ContinueStatement statement) { - escaping |= !outerStatements.contains(statement.getTarget()); } @Override From ff9324570cafdb48d4de205bcd347ccee9acd2b8 Mon Sep 17 00:00:00 2001 From: Steve Hannah Date: Tue, 28 Apr 2015 10:38:01 -0700 Subject: [PATCH 09/16] Fixed issue https://github.com/konsoletyper/teavm/issues/100 --- .../org/teavm/classlib/java/io/TDataInputStream.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TDataInputStream.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TDataInputStream.java index 830eea1b1..acbf748b6 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TDataInputStream.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TDataInputStream.java @@ -240,13 +240,14 @@ public class TDataInputStream extends TFilterInputStream implements TDataInput { int a; while (count < utfSize) { char ch = (char)buf[offset + count++]; + out[s] = ch; if (ch < '\u0080') { - out[s++] = ch; + s++; } else if (((a = out[s]) & 0xe0) == 0xc0) { if (count >= utfSize) { throw new TUTFDataFormatException(TString.wrap("End of stream reached")); } - int b = buf[count++]; + int b = buf[offset + count++]; if ((b & 0xC0) != 0x80) { throw new TUTFDataFormatException(TString.wrap("Malformed UTF-8 sequence")); } @@ -255,8 +256,8 @@ public class TDataInputStream extends TFilterInputStream implements TDataInput { if (count + 1 >= utfSize) { throw new TUTFDataFormatException(TString.wrap("Malformed UTF-8 sequence")); } - int b = buf[count++]; - int c = buf[count++]; + int b = buf[offset + count++]; + int c = buf[offset + count++]; if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) { throw new TUTFDataFormatException(TString.wrap("Malformed UTF-8 sequence")); } From 98e21423325656be946518d987f45b4f82a64292 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 30 Apr 2015 15:03:47 +0400 Subject: [PATCH 10/16] Fix performance regression. See https://github.com/konsoletyper/teavm/issues/102 --- .../org/teavm/javascript/BreakEliminator.java | 2 +- .../javascript/EscapingStatementFinder.java | 102 +++++++++--------- 2 files changed, 49 insertions(+), 55 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java index bff4928d8..db0420473 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/BreakEliminator.java @@ -156,6 +156,6 @@ class BreakEliminator implements StatementVisitor { } private boolean escapes(List statements) { - return new EscapingStatementFinder().check(statements); + return new EscapingStatementFinder(usageCounter).check(statements); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java b/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java index 921ac1142..6ee00fe5e 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/EscapingStatementFinder.java @@ -15,9 +15,7 @@ */ package org.teavm.javascript; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.teavm.javascript.ast.*; /** @@ -25,39 +23,48 @@ import org.teavm.javascript.ast.*; * @author Alexey Andreev */ class EscapingStatementFinder implements StatementVisitor { + AllBlocksCountVisitor blockCountVisitor; public boolean escaping; - private boolean last = true; - private Set outerStatements = new HashSet<>(); - private Set breakTargets = new HashSet<>(); - private IdentifiedStatement currentBlock; + + public EscapingStatementFinder(AllBlocksCountVisitor blockCountVisitor) { + this.blockCountVisitor = blockCountVisitor; + } + + private boolean isEmpty(Statement statement) { + if (!(statement instanceof SequentialStatement)) { + return false; + } + SequentialStatement seq = (SequentialStatement)statement; + for (int i = seq.getSequence().size() - 1; i >= 0; --i) { + if (!isEmpty(seq.getSequence().get(i))) { + return false; + } + } + return true; + } public boolean check(List statements) { if (escaping) { return true; } if (statements.isEmpty()) { - escaping = last; + escaping = true; return true; } - boolean oldLast = last; - for (int i = 0; i < statements.size(); ++i) { - last = false; - statements.get(i).acceptVisitor(this); - if (escaping) { - break; + for (int i = statements.size() - 1; i >= 0; --i) { + Statement stmt = statements.get(i); + if (!isEmpty(stmt)) { + stmt.acceptVisitor(this); + return escaping; } } - last = oldLast; - if (!escaping) { - statements.get(statements.size() - 1).acceptVisitor(this); - } - last = oldLast; - return escaping; + escaping = true; + return true; } @Override public void visit(AssignmentStatement statement) { - escaping |= last; + escaping |= true; } @Override @@ -74,56 +81,42 @@ class EscapingStatementFinder implements StatementVisitor { @Override public void visit(SwitchStatement statement) { - IdentifiedStatement oldCurrentBlock = currentBlock; - currentBlock = statement; - outerStatements.add(statement); + if (blockCountVisitor.getCount(statement) > 0) { + escaping = true; + return; + } for (SwitchClause clause : statement.getClauses()) { if (check(clause.getBody())) { break; } } - check(statement.getDefaultClause()); - outerStatements.remove(statement); - currentBlock = oldCurrentBlock; - if (breakTargets.contains(statement)) { - escaping |= last; + if (!escaping) { + check(statement.getDefaultClause()); } } @Override public void visit(WhileStatement statement) { - IdentifiedStatement oldCurrentBlock = currentBlock; - currentBlock = statement; - outerStatements.add(statement); - if (!check(statement.getBody()) && statement.getCondition() != null) { - escaping |= last; + if (blockCountVisitor.getCount(statement) > 0) { + escaping = true; + return; } - outerStatements.remove(statement); - currentBlock = oldCurrentBlock; - if (breakTargets.contains(statement)) { - escaping |= last; + if (statement.getCondition() != null && check(statement.getBody())) { + escaping = true; } } @Override public void visit(BlockStatement statement) { - IdentifiedStatement oldCurrentBlock = currentBlock; - currentBlock = statement; - outerStatements.add(statement); - check(statement.getBody()); - outerStatements.remove(statement); - currentBlock = oldCurrentBlock; - if (breakTargets.contains(statement)) { - escaping |= last; + if (blockCountVisitor.getCount(statement) > 0) { + escaping = true; + return; } + check(statement.getBody()); } @Override public void visit(BreakStatement statement) { - IdentifiedStatement target = statement.getTarget() != null ? statement.getTarget() : currentBlock; - if (target != null) { - breakTargets.add(target); - } } @Override @@ -140,13 +133,14 @@ class EscapingStatementFinder implements StatementVisitor { @Override public void visit(InitClassStatement statement) { - escaping |= last; + escaping = true; } @Override public void visit(TryCatchStatement statement) { - check(statement.getProtectedBody()); - check(statement.getHandler()); + if (!check(statement.getProtectedBody())) { + check(statement.getHandler()); + } } @Override @@ -155,11 +149,11 @@ class EscapingStatementFinder implements StatementVisitor { @Override public void visit(MonitorEnterStatement statement) { - escaping |= last; + escaping = true; } @Override public void visit(MonitorExitStatement statement) { - escaping |= last; + escaping = true; } } From 7b029d024caff90bc75506cf189b269ffe15028a Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 1 May 2015 10:49:51 +0300 Subject: [PATCH 11/16] Upgrade to ASM5 API --- .../org/teavm/classlib/impl/report/JCLComparisonVisitor.java | 2 +- teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/report/JCLComparisonVisitor.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/report/JCLComparisonVisitor.java index c82b298b0..3e8ebf29b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/report/JCLComparisonVisitor.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/report/JCLComparisonVisitor.java @@ -32,7 +32,7 @@ class JCLComparisonVisitor extends ClassVisitor { private JCLClass jclClass; public JCLComparisonVisitor(ClassReaderSource classSource, Map packageMap) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.classSource = classSource; this.packageMap = packageMap; } 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 a379c459a..123619605 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -367,7 +367,7 @@ public class ProgramParser implements VariableDebugInformation { } // TODO: invokedynamic support (a great task, involving not only parser, but every layer of TeaVM) - private MethodVisitor methodVisitor = new MethodVisitor(Opcodes.ASM4) { + private MethodVisitor methodVisitor = new MethodVisitor(Opcodes.ASM5) { @Override public void visitVarInsn(int opcode, int local) { switch (opcode) { From 26379afc50042833f2d45c9b4e05493e11798df8 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 1 May 2015 15:19:35 +0300 Subject: [PATCH 12/16] Fix bug in bytecode parser. Fix https://github.com/konsoletyper/teavm/issues/104 Fix https://github.com/konsoletyper/teavm/issues/105 --- .../src/main/java/org/teavm/classlib/java/lang/TMath.java | 2 +- .../src/main/java/org/teavm/parsing/ProgramParser.java | 7 ++++++- .../src/main/resources/org/teavm/javascript/runtime.js | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java index 016b67253..b64d4305b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java @@ -93,7 +93,7 @@ public final class TMath extends TObject { public static native double atan2(double y, double x); public static int round(float a) { - return (int)(a + 1.5f); + return (int)(a + 0.5f); } public static long round(double a) { 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 123619605..c4205da2d 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -485,7 +485,12 @@ public class ProgramParser implements VariableDebugInformation { } @Override - public void visitMethodInsn(int opcode, String owner, String name, String desc) { + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { + throw new IllegalStateException("InvokeDynamic is not supported in TeaVM"); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { switch (opcode) { case Opcodes.INVOKEINTERFACE: case Opcodes.INVOKEVIRTUAL: 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 142d1cc95..759869cbb 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -250,6 +250,14 @@ function $rt_createByteMultiArray(dimensions) { } return $rt_createMultiArrayImpl($rt_bytecls(), arrays, dimensions); } +function $rt_createCharMultiArray(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_createCharArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_charcls(), arrays, dimensions); +} function $rt_createBooleanMultiArray(dimensions) { var arrays = new Array($rt_primitiveArrayCount(dimensions)); var firstDim = dimensions[0] | 0; From 7c916043118c980bdfd3a97df744f0431827fdb3 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 4 May 2015 11:42:07 +0300 Subject: [PATCH 13/16] Add StackTraceElement stub to run Kotlin --- .../java/lang/TStackTraceElement.java | 94 +++++++++++++++++++ .../org/teavm/classlib/java/lang/TThread.java | 4 + .../teavm/classlib/java/lang/TThrowable.java | 9 ++ 3 files changed, 107 insertions(+) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStackTraceElement.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStackTraceElement.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStackTraceElement.java new file mode 100644 index 000000000..2ae62814a --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStackTraceElement.java @@ -0,0 +1,94 @@ +/* + * 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 java.util.Objects; +import org.teavm.classlib.java.io.TSerializable; +import org.teavm.classlib.java.util.TObjects; + +/** + * + * @author Alexey Andreev + */ +public final class TStackTraceElement extends TObject implements TSerializable { + private TString declaringClass; + private TString methodName; + private TString fileName; + private int lineNumber; + + public TStackTraceElement(TString declaringClass, TString methodName, TString fileName, int lineNumber) { + if (declaringClass == null || methodName == null) { + throw new TNullPointerException(); + } + this.declaringClass = declaringClass; + this.methodName = methodName; + this.fileName = fileName; + this.lineNumber = lineNumber; + } + + public TString getClassName() { + return declaringClass; + } + + public TString getMethodName() { + return methodName; + } + + public TString getFileName() { + return fileName; + } + + public int getLineNumber() { + return lineNumber; + } + + public boolean isNativeMethod() { + return fileName == null; + } + + @Override + public int hashCode() { + return Objects.hash(declaringClass, methodName, fileName, lineNumber); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof TStackTraceElement)) { + return false; + } + TStackTraceElement other = (TStackTraceElement)obj; + return TObjects.equals(declaringClass, other.declaringClass) && + TObjects.equals(methodName, other.methodName) && + TObjects.equals(fileName, other.fileName) && + lineNumber == other.lineNumber; + } + + @Override + public String toString() { + TStringBuilder sb = new TStringBuilder(); + int index = declaringClass.lastIndexOf('.'); + sb.append(declaringClass.substring(index + 1)).append('.').append(methodName).append('('); + if (fileName != null) { + sb.append(fileName).append(':').append(lineNumber); + } else { + sb.append(TString.wrap("Unknown Source")); + } + return sb.toString(); + } +} 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 4b98842c1..c4de1d645 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 @@ -167,4 +167,8 @@ public class TThread extends TObject implements TRunnable { public final int getPriority(){ return this.priority; } + + public TStackTraceElement[] getStackTrace() { + return new TStackTraceElement[0]; + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java index 6471e8440..70ab398d6 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java @@ -146,6 +146,15 @@ public class TThrowable extends RuntimeException { stream.println(TString.wrap(getClass().getName() + ": " + getMessage())); } + @Rename("getStackTrace") + public TStackTraceElement[] getStackTrace0() { + return new TStackTraceElement[0]; + } + + public void setStackTrace(@SuppressWarnings("unused") TStackTraceElement[] stackTrace) { + // do nothing + } + @Rename("getSuppressed") public final TThrowable[] getSuppressed0() { return TArrays.copyOf(suppressed, suppressed.length); From 77eba4c78a1a1a21d1feed7b0cb66b08b91e6ce4 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 4 May 2015 12:33:42 +0300 Subject: [PATCH 14/16] Add Kotlin example --- teavm-samples/pom.xml | 1 + teavm-samples/teavm-samples-kotlin/.gitignore | 4 + teavm-samples/teavm-samples-kotlin/pom.xml | 131 ++++++++++++++++++ .../src/main/kotlin/Hello.kt | 15 ++ .../src/main/webapp/WEB-INF/web.xml | 21 +++ .../src/main/webapp/index.html | 31 +++++ 6 files changed, 203 insertions(+) create mode 100644 teavm-samples/teavm-samples-kotlin/.gitignore create mode 100644 teavm-samples/teavm-samples-kotlin/pom.xml create mode 100644 teavm-samples/teavm-samples-kotlin/src/main/kotlin/Hello.kt create mode 100644 teavm-samples/teavm-samples-kotlin/src/main/webapp/WEB-INF/web.xml create mode 100644 teavm-samples/teavm-samples-kotlin/src/main/webapp/index.html diff --git a/teavm-samples/pom.xml b/teavm-samples/pom.xml index 6513d89c4..4dd090a69 100644 --- a/teavm-samples/pom.xml +++ b/teavm-samples/pom.xml @@ -35,5 +35,6 @@ teavm-samples-storage teavm-samples-video teavm-samples-async + teavm-samples-kotlin \ No newline at end of file diff --git a/teavm-samples/teavm-samples-kotlin/.gitignore b/teavm-samples/teavm-samples-kotlin/.gitignore new file mode 100644 index 000000000..8bd3a0588 --- /dev/null +++ b/teavm-samples/teavm-samples-kotlin/.gitignore @@ -0,0 +1,4 @@ +/target/ +/.settings/ +/.classpath +/.project diff --git a/teavm-samples/teavm-samples-kotlin/pom.xml b/teavm-samples/teavm-samples-kotlin/pom.xml new file mode 100644 index 000000000..7e7147a5d --- /dev/null +++ b/teavm-samples/teavm-samples-kotlin/pom.xml @@ -0,0 +1,131 @@ + + + 4.0.0 + + + org.teavm + teavm-samples + 0.3.0-SNAPSHOT + + teavm-samples-kotlin + + war + + TeaVM Kotlin web application + A sample application written in Kotlin and compiled by TeaVM + + + 0.11.91.1 + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + junit + junit + test + + + org.teavm + teavm-classlib + ${project.version} + provided + + + org.teavm + teavm-dom + ${project.version} + provided + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.teavm + teavm-maven-plugin + ${project.version} + + + web-client + prepare-package + + build-javascript + + + ${project.build.directory}/generated/js/teavm + org.teavm.samples.kotlin.KotlinPackage + SEPARATE + false + true + true + true + + + + + + maven-war-plugin + 2.4 + + + + ${project.build.directory}/generated/js + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + \ No newline at end of file diff --git a/teavm-samples/teavm-samples-kotlin/src/main/kotlin/Hello.kt b/teavm-samples/teavm-samples-kotlin/src/main/kotlin/Hello.kt new file mode 100644 index 000000000..f71b3bcf6 --- /dev/null +++ b/teavm-samples/teavm-samples-kotlin/src/main/kotlin/Hello.kt @@ -0,0 +1,15 @@ +package org.teavm.samples.kotlin + +import org.teavm.jso.* +import org.teavm.dom.browser.* +import org.teavm.dom.html.* +import org.teavm.dom.events.* + +fun main(args : Array) { + var window = JS.getGlobal() as Window; + var document = window.getDocument(); + + document.getElementById("hello-kotlin").addEventListener("click", EventListener() { + window.alert("Hello, developer!"); + }) +} diff --git a/teavm-samples/teavm-samples-kotlin/src/main/webapp/WEB-INF/web.xml b/teavm-samples/teavm-samples-kotlin/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..bfc410b12 --- /dev/null +++ b/teavm-samples/teavm-samples-kotlin/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/teavm-samples/teavm-samples-kotlin/src/main/webapp/index.html b/teavm-samples/teavm-samples-kotlin/src/main/webapp/index.html new file mode 100644 index 000000000..988fa4e9a --- /dev/null +++ b/teavm-samples/teavm-samples-kotlin/src/main/webapp/index.html @@ -0,0 +1,31 @@ + + + + Hello kotlin + + + + + +

+ +

This application was compiled by TeaVM from the following Kotlin source:

+
+package org.teavm.samples.kotlin
+
+import org.teavm.jso.*
+import org.teavm.dom.browser.*
+import org.teavm.dom.html.*
+import org.teavm.dom.events.*
+
+fun main(args : Array<String>) {
+    var window = JS.getGlobal() as Window;
+    var document = window.getDocument();
+
+    document.getElementById("hello-kotlin").addEventListener("click", EventListener<MouseEvent>() {
+       window.alert("Hello, developer!");
+    })
+}
+    
+ + \ No newline at end of file From b4e4dd8d4380720e09ea8bfc8bf774f8eb8fd049 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 4 May 2015 16:09:16 +0300 Subject: [PATCH 15/16] Omit unnecessary continue $main --- .../java/org/teavm/javascript/Renderer.java | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) 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 cd89dc1d6..41591f971 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -67,6 +67,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext private Map blockIdMap = new HashMap<>(); private List> debugNames = new ArrayList<>(); private List cachedVariableNames = new ArrayList<>(); + private boolean end; + private int currentPart; private static class OperatorPrecedence { Priority priority; @@ -657,6 +659,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } writer.append(";").softNewLine(); } + end = true; + currentPart = 0; method.getBody().acceptVisitor(Renderer.this); } catch (IOException e) { throw new RenderingException("IO error occured", e); @@ -743,6 +747,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext emitSuspendChecker(); } AsyncMethodPart part = methodNode.getBody().get(i); + end = true; + currentPart = i; part.getStatement().acceptVisitor(Renderer.this); writer.outdent(); } @@ -901,9 +907,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext @Override public void visit(SequentialStatement statement) { - for (Statement part : statement.getSequence()) { - part.acceptVisitor(this); - } + visitStatements(statement.getSequence()); } @Override @@ -924,9 +928,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } debugEmitter.emitCallSite(); writer.append(")").ws().append("{").softNewLine().indent(); - for (Statement part : statement.getConsequent()) { - part.acceptVisitor(this); - } + visitStatements(statement.getConsequent()); if (!statement.getAlternative().isEmpty()) { writer.outdent().append("}").ws(); if (statement.getAlternative().size() == 1 && @@ -936,9 +938,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext continue; } writer.append("else").ws().append("{").indent().softNewLine(); - for (Statement part : statement.getAlternative()) { - part.acceptVisitor(this); - } + visitStatements(statement.getAlternative()); } break; } @@ -973,16 +973,22 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append("case ").append(condition).append(":").softNewLine(); } writer.indent(); + boolean oldEnd = end; for (Statement part : clause.getBody()) { + end = false; part.acceptVisitor(this); } + end = oldEnd; writer.outdent(); } if (statement.getDefaultClause() != null) { writer.append("default:").softNewLine().indent(); + boolean oldEnd = end; for (Statement part : statement.getDefaultClause()) { + end = false; part.acceptVisitor(this); } + end = oldEnd; writer.outdent(); } writer.outdent().append("}").softNewLine(); @@ -1015,9 +1021,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append("true"); } writer.append(")").ws().append("{").softNewLine().indent(); + boolean oldEnd = end; for (Statement part : statement.getBody()) { + end = false; part.acceptVisitor(this); } + end = oldEnd; writer.outdent().append("}").softNewLine(); } catch (IOException e) { throw new RenderingException("IO error occured", e); @@ -1047,9 +1056,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext public void visit(BlockStatement statement) { try { writer.append(mapBlockId(statement.getId())).append(":").ws().append("{").softNewLine().indent(); - for (Statement part : statement.getBody()) { - part.acceptVisitor(this); - } + visitStatements(statement.getBody()); writer.outdent().append("}").softNewLine(); } catch (IOException e) { throw new RenderingException("IO error occured", e); @@ -2019,6 +2026,17 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } } + private void visitStatements(List statements) { + boolean oldEnd = end; + for (int i = 0; i < statements.size() - 1; ++i) { + end = false; + statements.get(i).acceptVisitor(this); + } + end = oldEnd; + statements.get(statements.size() - 1).acceptVisitor(this); + end = oldEnd; + } + @Override public void visit(TryCatchStatement statement) { try { @@ -2031,9 +2049,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext sequence.add(nextStatement); protectedBody = nextStatement.getProtectedBody(); } - for (Statement part : protectedBody) { - part.acceptVisitor(this); - } + visitStatements(protectedBody); writer.outdent().append("}").ws().append("catch").ws().append("($e)") .ws().append("{").indent().softNewLine(); writer.append("$je").ws().append("=").ws().append("$e.$javaException;").softNewLine(); @@ -2048,9 +2064,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append(variableName(catchClause.getExceptionVariable())).ws().append("=").ws() .append("$je;").softNewLine(); } - for (Statement part : catchClause.getHandler()) { - part.acceptVisitor(this); - } + visitStatements(catchClause.getHandler()); writer.outdent().append("}").ws().append("else "); } writer.append("{").indent().softNewLine(); @@ -2067,7 +2081,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext try { writer.append(pointerName()).ws().append("=").ws().append(statement.getPart()).append(";") .softNewLine(); - writer.append("continue ").append(mainLoopName()).append(";").softNewLine(); + if (!end || statement.getPart() != currentPart + 1) { + writer.append("continue ").append(mainLoopName()).append(";").softNewLine(); + } } catch (IOException ex){ throw new RenderingException("IO error occured", ex); } From 11d69f15c9081b405d95f81f2c6402a4dfc50c56 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 4 May 2015 16:50:36 +0300 Subject: [PATCH 16/16] If possible, move continue $main to the end of the async part, giving a chance to eliminate it. --- .../java/org/teavm/common/GraphIndexer.java | 56 ++++++++++++++++--- .../java/org/teavm/javascript/Decompiler.java | 8 ++- .../java/org/teavm/javascript/Renderer.java | 6 +- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/common/GraphIndexer.java b/teavm-core/src/main/java/org/teavm/common/GraphIndexer.java index 21f1b94c6..f1b2707c2 100644 --- a/teavm-core/src/main/java/org/teavm/common/GraphIndexer.java +++ b/teavm-core/src/main/java/org/teavm/common/GraphIndexer.java @@ -37,11 +37,14 @@ public class GraphIndexer { private DominatorTree domTree; private int lastIndex; private int[] weights; + private int[] priorities; - public GraphIndexer(Graph graph, int[] weights) { + public GraphIndexer(Graph graph, int[] weights, int[] priorities) { int sz = graph.size(); - this.weights = weights; - propagateWeights(graph, weights); + this.weights = weights.clone(); + propagateWeights(graph, this.weights); + this.priorities = priorities.clone(); + propagatePriorities(graph, this.priorities); indexToNode = new int[sz + 1]; nodeToIndex = new int[sz + 1]; Arrays.fill(nodeToIndex, -1); @@ -99,6 +102,39 @@ public class GraphIndexer { } } + private void propagatePriorities(Graph graph, int[] priorities) { + boolean allZero = true; + for (int i = 0; i < priorities.length; ++i) { + if (priorities[i] != 0) { + allZero = false; + break; + } + } + if (allZero) { + return; + } + + DominatorTree domTree = GraphUtils.buildDominatorTree(graph); + Graph domGraph = GraphUtils.buildDominatorGraph(domTree, graph.size()); + IntegerStack stack = new IntegerStack(graph.size() * 2); + for (int i = 0; i < domGraph.size(); ++i) { + if (domGraph.outgoingEdgesCount(i) == 0) { + stack.push(i); + } + } + while (!stack.isEmpty()) { + int node = stack.pop(); + int parent = domTree.immediateDominatorOf(node); + if (parent < 0) { + continue; + } + if (priorities[parent] < priorities[node]) { + priorities[parent] = priorities[node]; + stack.push(parent); + } + } + } + private void sort(Graph graph) { int sz = graph.size(); byte[] state = new byte[sz]; @@ -128,7 +164,7 @@ public class GraphIndexer { IntSet loopNodes = IntOpenHashSet.from(findNaturalLoop(node, terminalNodes.getAll())); for (int succ : successors) { if (loopNodes.contains(succ)) { - succList.add(new WeightedNode(succ, weights[succ])); + succList.add(new WeightedNode(succ, priorities[succ], weights[succ])); } } Collections.sort(succList); @@ -142,7 +178,7 @@ public class GraphIndexer { for (int succ : graph.outgoingEdges(loopNode.value)) { if (!loopNodes.contains(succ)) { if (outerSuccessors.add(succ)) { - succList.add(new WeightedNode(succ, weights[succ])); + succList.add(new WeightedNode(succ, priorities[succ], weights[succ])); } } } @@ -153,7 +189,7 @@ public class GraphIndexer { } } else { for (int succ : successors) { - succList.add(new WeightedNode(succ, weights[succ])); + succList.add(new WeightedNode(succ, priorities[succ], weights[succ])); } Collections.sort(succList); for (WeightedNode wnode : succList) { @@ -209,15 +245,21 @@ public class GraphIndexer { static class WeightedNode implements Comparable { int index; + int priority; int weight; - public WeightedNode(int index, int weight) { + public WeightedNode(int index, int priority, int weight) { this.index = index; + this.priority = priority; this.weight = weight; } @Override public int compareTo(WeightedNode o) { + int r = Integer.compare(priority, o.priority); + if (r != 0) { + return r; + } return Integer.compare(weight, o.weight); } } 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 97e19d484..ca3ba41be 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -338,7 +338,13 @@ public class Decompiler { for (int i = 0; i < weights.length; ++i) { weights[i] = program.basicBlockAt(i).getInstructions().size(); } - indexer = new GraphIndexer(graph, weights); + int[] priorities = new int[graph.size()]; + for (int i = 0; i < targetBlocks.length; ++i) { + if (targetBlocks[i] >= 0) { + priorities[i] = 1; + } + } + indexer = new GraphIndexer(graph, weights, priorities); graph = indexer.getGraph(); loopGraph = new LoopGraph(this.graph); unflatCode(); 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 41591f971..3451cd4c8 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -2079,8 +2079,10 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext @Override public void visit(GotoPartStatement statement) { try { - writer.append(pointerName()).ws().append("=").ws().append(statement.getPart()).append(";") - .softNewLine(); + if (statement.getPart() != currentPart) { + writer.append(pointerName()).ws().append("=").ws().append(statement.getPart()).append(";") + .softNewLine(); + } if (!end || statement.getPart() != currentPart + 1) { writer.append("continue ").append(mainLoopName()).append(";").softNewLine(); }