Merge branch 'master' into add_timezone

This commit is contained in:
Steve Hannah 2015-05-04 12:31:42 -07:00
commit cc8a5a6514
22 changed files with 711 additions and 100 deletions

View File

@ -32,7 +32,7 @@ class JCLComparisonVisitor extends ClassVisitor {
private JCLClass jclClass;
public JCLComparisonVisitor(ClassReaderSource classSource, Map<String, JCLPackage> packageMap) {
super(Opcodes.ASM4);
super(Opcodes.ASM5);
this.classSource = classSource;
this.packageMap = packageMap;
}

View File

@ -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"));
}

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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];
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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<WeightedNode> {
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);
}
}

View File

@ -27,14 +27,24 @@ import org.teavm.javascript.ast.*;
class AllBlocksCountVisitor implements StatementVisitor {
private Map<IdentifiedStatement, Integer> blocksCount = new HashMap<>();
private IdentifiedStatement currentBlock;
private boolean last = true;
public void visit(List<Statement> 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();
}
}
}

View File

@ -26,7 +26,6 @@ class BreakEliminator implements StatementVisitor {
private Map<BlockStatement, List<Statement>> blockSuccessors = new HashMap<>();
private Set<IdentifiedStatement> outerStatements = new HashSet<>();
private List<Statement> currentSequence;
private boolean sequenceEscapes;
private int currentIndex;
private AllBlocksCountVisitor usageCounter;
@ -39,15 +38,12 @@ class BreakEliminator implements StatementVisitor {
private void processSequence(List<Statement> statements) {
List<Statement> 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;
}
}
@ -143,9 +138,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
@ -161,6 +156,6 @@ class BreakEliminator implements StatementVisitor {
}
private boolean escapes(List<Statement> statements) {
return new EscapingStatementFinder(outerStatements).check(statements);
return new EscapingStatementFinder(usageCounter).check(statements);
}
}

View File

@ -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;
@ -47,6 +90,9 @@ public class Decompiler {
private MethodNodeCache regularMethodCache;
private Set<MethodReference> asyncMethods;
private Set<MethodReference> splitMethods = new HashSet<>();
private List<TryCatchBookmark> tryCatchBookmarks = new ArrayList<>();
private Deque<Block> stack;
private Program program;
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set<MethodReference> asyncMethods,
Set<MethodReference> asyncFamilyMethods) {
@ -70,10 +116,13 @@ public class Decompiler {
}
static class Block {
public Block parent;
public int parentOffset;
public final IdentifiedStatement statement;
public final List<Statement> body;
public final int end;
public final int start;
public final List<TryCatchBookmark> tryCatches = new ArrayList<>();
public Block(IdentifiedStatement statement, List<Statement> body, int start, int end) {
this.statement = statement;
@ -83,6 +132,14 @@ public class Decompiler {
}
}
static class TryCatchBookmark {
Block block;
int offset;
String exceptionType;
Integer exceptionVariable;
int exceptionHandler;
}
public List<ClassNode> decompile(Collection<String> classNames) {
List<String> sequence = new ArrayList<>();
Set<String> visited = new HashSet<>();
@ -276,17 +333,24 @@ 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();
}
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();
blockMap = new Block[program.basicBlockCount() * 2 + 1];
Deque<Block> 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 +363,22 @@ 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;
}
closeExpiredBookmarks(generator, generator.currentBlock.getTryCatchBlocks());
List<TryCatchBookmark> inheritedBookmarks = new ArrayList<>();
Block block = stack.peek();
while (block.end == i) {
Block oldBlock = block;
@ -310,26 +390,46 @@ 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(bookmark.exceptionVariable);
tryCatchStmt.getHandler().add(generator.generateJumpStatement(
program.basicBlockAt(bookmark.exceptionHandler)));
List<Statement> blockPart = oldBlock.body.subList(bookmark.offset, oldBlock.body.size());
tryCatchStmt.getProtectedBody().addAll(blockPart);
blockPart.clear();
if (!tryCatchStmt.getProtectedBody().isEmpty()) {
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;
newBlock.parentOffset = block.body.size();
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;
}
createNewBookmarks(generator.currentBlock.getTryCatchBlocks());
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 +451,6 @@ 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);
}
}
block.body.addAll(generator.statements);
}
}
@ -372,6 +460,77 @@ public class Decompiler {
return result;
}
private void closeExpiredBookmarks(StatementGenerator generator, List<TryCatchBlock> 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;
}
}
// 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(bookmark.exceptionVariable);
tryCatchStmt.getHandler().add(generator.generateJumpStatement(
program.basicBlockAt(bookmark.exceptionHandler)));
tryCatchStmt.getProtectedBody().addAll(block.body);
block.body.clear();
if (!tryCatchStmt.getProtectedBody().isEmpty()) {
block.body.add(tryCatchStmt);
}
block = block.parent;
}
TryCatchStatement tryCatchStmt = new TryCatchStatement();
tryCatchStmt.setExceptionType(bookmark.exceptionType);
tryCatchStmt.setExceptionVariable(bookmark.exceptionVariable);
tryCatchStmt.getHandler().add(generator.generateJumpStatement(
program.basicBlockAt(bookmark.exceptionHandler)));
List<Statement> blockPart = block.body.subList(bookmark.offset, block.body.size());
tryCatchStmt.getProtectedBody().addAll(blockPart);
blockPart.clear();
if (!tryCatchStmt.getProtectedBody().isEmpty()) {
blockPart.add(tryCatchStmt);
}
bookmark.block.tryCatches.remove(bookmark);
}
tryCatchBookmarks.subList(start, tryCatchBookmarks.size()).clear();
}
private void createNewBookmarks(List<TryCatchBlock> tryCatchBlocks) {
// Add new bookmarks
for (int i = tryCatchBookmarks.size(); 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<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) {
Set<NodeModifier> result = EnumSet.noneOf(NodeModifier.class);
if (modifiers.contains(ElementModifier.STATIC)) {

View File

@ -16,7 +16,6 @@
package org.teavm.javascript;
import java.util.List;
import java.util.Set;
import org.teavm.javascript.ast.*;
/**
@ -24,27 +23,48 @@ import org.teavm.javascript.ast.*;
* @author Alexey Andreev
*/
class EscapingStatementFinder implements StatementVisitor {
AllBlocksCountVisitor blockCountVisitor;
public boolean escaping;
private Set<IdentifiedStatement> outerStatements;
public EscapingStatementFinder(Set<IdentifiedStatement> nestingStatements) {
this.outerStatements = nestingStatements;
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<Statement> statements) {
if (!escaping) {
if (escaping) {
return true;
}
if (statements.isEmpty()) {
escaping = true;
} else {
statements.get(statements.size() - 1).acceptVisitor(this);
}
return true;
}
for (int i = statements.size() - 1; i >= 0; --i) {
Statement stmt = statements.get(i);
if (!isEmpty(stmt)) {
stmt.acceptVisitor(this);
return escaping;
}
}
escaping = true;
return true;
}
@Override
public void visit(AssignmentStatement statement) {
escaping = true;
escaping |= true;
}
@Override
@ -61,36 +81,46 @@ class EscapingStatementFinder implements StatementVisitor{
@Override
public void visit(SwitchStatement statement) {
outerStatements.add(statement);
if (blockCountVisitor.getCount(statement) > 0) {
escaping = true;
return;
}
for (SwitchClause clause : statement.getClauses()) {
if (check(clause.getBody())) {
break;
}
}
if (!escaping) {
check(statement.getDefaultClause());
outerStatements.remove(statement);
}
}
@Override
public void visit(WhileStatement statement) {
if (blockCountVisitor.getCount(statement) > 0) {
escaping = true;
return;
}
if (statement.getCondition() != null && check(statement.getBody())) {
escaping = true;
}
}
@Override
public void visit(BlockStatement statement) {
outerStatements.add(statement);
if (blockCountVisitor.getCount(statement) > 0) {
escaping = true;
return;
}
check(statement.getBody());
outerStatements.remove(statement);
}
@Override
public void visit(BreakStatement statement) {
escaping = !outerStatements.contains(statement.getTarget());
}
@Override
public void visit(ContinueStatement statement) {
escaping = !outerStatements.contains(statement.getTarget());
}
@Override
@ -108,9 +138,10 @@ class EscapingStatementFinder implements StatementVisitor{
@Override
public void visit(TryCatchStatement statement) {
check(statement.getProtectedBody());
if (!check(statement.getProtectedBody())) {
check(statement.getHandler());
}
}
@Override
public void visit(GotoPartStatement statement) {

View File

@ -67,6 +67,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private Map<String, String> blockIdMap = new HashMap<>();
private List<Set<String>> debugNames = new ArrayList<>();
private List<String> 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<Statement> 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();
@ -2065,9 +2079,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(GotoPartStatement statement) {
try {
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();
}
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}

View File

@ -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;
}

View File

@ -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) {
@ -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:

View File

@ -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;

View File

@ -35,5 +35,6 @@
<module>teavm-samples-storage</module>
<module>teavm-samples-video</module>
<module>teavm-samples-async</module>
<module>teavm-samples-kotlin</module>
</modules>
</project>

View File

@ -0,0 +1,4 @@
/target/
/.settings/
/.classpath
/.project

View File

@ -0,0 +1,131 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.teavm</groupId>
<artifactId>teavm-samples</artifactId>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>teavm-samples-kotlin</artifactId>
<packaging>war</packaging>
<name>TeaVM Kotlin web application</name>
<description>A sample application written in Kotlin and compiled by TeaVM</description>
<properties>
<kotlin.version>0.11.91.1</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.teavm</groupId>
<artifactId>teavm-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>
<mainClass>org.teavm.samples.kotlin.KotlinPackage</mainClass>
<runtime>SEPARATE</runtime>
<minifying>false</minifying>
<debugInformationGenerated>true</debugInformationGenerated>
<sourceMapsGenerated>true</sourceMapsGenerated>
<sourceFilesCopied>true</sourceFilesCopied>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<webResources>
<resource>
<directory>${project.build.directory}/generated/js</directory>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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<String>) {
var window = JS.getGlobal() as Window;
var document = window.getDocument();
document.getElementById("hello-kotlin").addEventListener("click", EventListener<MouseEvent>() {
window.alert("Hello, developer!");
})
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2014 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.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>Hello kotlin</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script type="text/javascript" charset="utf-8" src="teavm/runtime.js"></script>
<script type="text/javascript" charset="utf-8" src="teavm/classes.js"></script>
</head>
<body onload="main()">
<p><button id="hello-kotlin">Hello, Kotlin!</button></p>
<p>This application was compiled by TeaVM from the following Kotlin source:</p>
<pre>
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&lt;String&gt;) {
var window = JS.getGlobal() as Window;
var document = window.getDocument();
document.getElementById("hello-kotlin").addEventListener("click", EventListener&lt;MouseEvent&gt;() {
window.alert("Hello, developer!");
})
}
</pre>
</body>
</html>