diff --git a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java index 39db3ae67..48d18d999 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java +++ b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java @@ -16,6 +16,10 @@ public class SourceWriter { this.naming = naming; } + public void clear() { + sb.setLength(0); + } + public SourceWriter append(String value) { appendIndent(); sb.append(value); @@ -34,6 +38,12 @@ public class SourceWriter { return this; } + public SourceWriter append(char value) { + appendIndent(); + sb.append(value); + return this; + } + public SourceWriter appendClass(String cls) { appendIndent(); sb.append(naming.getNameFor(cls)); @@ -69,7 +79,6 @@ public class SourceWriter { public SourceWriter indent() { ++indentSize; - newLine(); return this; } diff --git a/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java b/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java index 601f7a512..f95be8566 100644 --- a/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java @@ -12,19 +12,23 @@ import java.util.List; public class GraphBuilder { private GraphImpl builtGraph; private List addedEdges = new ArrayList<>(); + private int sz = 0; public GraphBuilder() { } public GraphBuilder(int sz) { addedEdges.addAll(Collections.nCopies(sz, null)); + this.sz = sz; } public void clear() { addedEdges.clear(); + sz = 0; } public void addEdge(int from, int to) { + sz = Math.max(sz, Math.max(from, to) + 1); builtGraph = null; if (addedEdges.size() == from) { addedEdges.add(IntegerArray.of(to)); @@ -43,11 +47,11 @@ public class GraphBuilder { public Graph build() { if (builtGraph == null) { - IntegerArray[] incomingEdges = new IntegerArray[addedEdges.size()]; - for (int i = 0; i < addedEdges.size(); ++i) { + IntegerArray[] incomingEdges = new IntegerArray[sz]; + for (int i = 0; i < sz; ++i) { incomingEdges[i] = new IntegerArray(1); } - int[][] outgoingEdgeList = new int[addedEdges.size()][]; + int[][] outgoingEdgeList = new int[sz][]; for (int i = 0; i < addedEdges.size(); ++i) { IntegerArray edgeList = addedEdges.get(i); outgoingEdgeList[i] = edgeList != null ? edgeList.getAll() : new int[0]; @@ -55,8 +59,11 @@ public class GraphBuilder { incomingEdges[j].add(i); } } - int[][] incomingEdgeList = new int[addedEdges.size()][]; - for (int i = 0; i < addedEdges.size(); ++i) { + for (int i = addedEdges.size(); i < sz; ++i) { + outgoingEdgeList[i] = new int[0]; + } + int[][] incomingEdgeList = new int[sz][]; + for (int i = 0; i < sz; ++i) { incomingEdgeList[i] = incomingEdges[i].getAll(); } builtGraph = new GraphImpl(incomingEdgeList, outgoingEdgeList); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ConditionalOptimizer.java b/teavm-core/src/main/java/org/teavm/javascript/ConditionalOptimizer.java new file mode 100644 index 000000000..c591eac07 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ConditionalOptimizer.java @@ -0,0 +1,228 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import java.util.Map; +import org.teavm.javascript.ast.*; + +/** + * + * @author Alexey Andreev + */ +class ConditionalOptimizer { + public Map referencedStatements; + public ReadWriteStatsBuilder stats; + + public Statement tryOptimizeElse(BlockStatement stmt) { + if (stmt.getBody().isEmpty()) { + return stmt; + } + if (!(stmt.getBody().get(0) instanceof ConditionalStatement)) { + return stmt; + } + ConditionalStatement condStmt = (ConditionalStatement)stmt.getBody().get(0); + if (condStmt.getAlternative() != null) { + return stmt; + } + if (!(condStmt.getConsequent() instanceof SequentialStatement)) { + return stmt; + } + SequentialStatement condBody = (SequentialStatement)condStmt.getConsequent(); + if (condBody.getSequence().isEmpty()) { + return stmt; + } + Statement lastStmt = condBody.getSequence().get(condBody.getSequence().size() - 1); + if (!(lastStmt instanceof BreakStatement)) { + return stmt; + } + BreakStatement breakStmt = (BreakStatement)lastStmt; + if (breakStmt.getTarget() != stmt) { + return stmt; + } + SequentialStatement altBody = new SequentialStatement(); + for (int i = 1; i < stmt.getBody().size(); ++i) { + altBody.getSequence().add(stmt.getBody().get(i)); + } + if (!altBody.getSequence().isEmpty()) { + condStmt.setAlternative(altBody.getSequence().size() != 1 ? + altBody : altBody.getSequence().get(0)); + } + condBody.getSequence().remove(condBody.getSequence().size() - 1); + if (condBody.getSequence().size() == 1) { + condStmt.setConsequent(condBody.getSequence().get(0)); + } + stmt.getBody().clear(); + stmt.getBody().add(condStmt); + referencedStatements.put(stmt, referencedStatements.get(stmt) - 1); + if (referencedStatements.get(stmt) > 0) { + return stmt; + } else { + return tryMakeInline(condStmt); + } + } + + public Statement tryOptimize(BlockStatement stmt) { + Expr condition = null; + while (true) { + if (stmt.getBody().isEmpty()) { + break; + } + if (!(stmt.getBody().get(0) instanceof ConditionalStatement)) { + break; + } + ConditionalStatement condStmt = (ConditionalStatement)stmt.getBody().get(0); + if (condStmt.getAlternative() != null) { + break; + } + if (!(condStmt.getConsequent() instanceof BreakStatement)) { + break; + } + BreakStatement breakStmt = (BreakStatement)condStmt.getConsequent(); + if (breakStmt.getTarget() != stmt) { + break; + } + stmt.getBody().remove(0); + if (condition == null) { + condition = ExprOptimizer.invert(condStmt.getCondition()); + } else { + condition = Expr.binary(BinaryOperation.AND, condition, + ExprOptimizer.invert(condStmt.getCondition())); + } + referencedStatements.put(stmt, referencedStatements.get(stmt) - 1); + } + if (condition == null) { + return stmt; + } + ConditionalStatement newCond = new ConditionalStatement(); + newCond.setCondition(condition); + if (referencedStatements.get(stmt) > 0) { + newCond.setConsequent(stmt); + } else { + if (stmt.getBody().size() == 1) { + newCond.setConsequent(stmt.getBody().get(0)); + } else { + SequentialStatement consequent = new SequentialStatement(); + consequent.getSequence().addAll(stmt.getBody()); + newCond.setConsequent(consequent); + } + } + return newCond; + } + + public void tryOptimize(WhileStatement stmt) { + if (stmt.getBody().isEmpty()) { + return; + } + if (!(stmt.getBody().get(0) instanceof ConditionalStatement)) { + return; + } + if (stmt.getCondition() != null) { + return; + } + ConditionalStatement condStmt = (ConditionalStatement)stmt.getBody().get(0); + if (condStmt.getAlternative() != null) { + return; + } + if (!(condStmt.getConsequent() instanceof BreakStatement)) { + return; + } + BreakStatement breakStmt = (BreakStatement)condStmt.getConsequent(); + if (breakStmt.getTarget() != stmt) { + return; + } + stmt.getBody().remove(0); + stmt.setCondition(ExprOptimizer.invert(condStmt.getCondition())); + } + + public Statement tryMakeInline(ConditionalStatement stmt) { + if (!(stmt.getConsequent() instanceof AssignmentStatement) || + !(stmt.getAlternative() instanceof AssignmentStatement)) { + return stmt; + } + AssignmentStatement consequent = (AssignmentStatement)stmt.getConsequent(); + AssignmentStatement alternative = (AssignmentStatement)stmt.getAlternative(); + if (!(consequent.getLeftValue() instanceof VariableExpr) || + !(alternative.getLeftValue() instanceof VariableExpr)) { + return stmt; + } + VariableExpr consequentLeft = (VariableExpr)consequent.getLeftValue(); + VariableExpr alternativeLeft = (VariableExpr)alternative.getLeftValue(); + if (consequentLeft.getIndex() != alternativeLeft.getIndex()) { + return stmt; + } + AssignmentStatement result = new AssignmentStatement(); + result.setLeftValue(consequentLeft); + ConditionalExpr rightValue = new ConditionalExpr(); + rightValue.setCondition(stmt.getCondition()); + rightValue.setConsequent(consequent.getRightValue()); + rightValue.setAlternative(alternative.getRightValue()); + result.setRightValue(rightValue); + stats.writes[consequentLeft.getIndex()]--; + return result; + } + + public Statement tryOptimizeSwitch(BlockStatement stmt) { + if (stmt.getBody().size() < 2) { + return stmt; + } + if (!(stmt.getBody().get(0) instanceof SwitchStatement)) { + return stmt; + } + SwitchStatement switchStmt = (SwitchStatement)stmt.getBody().get(0); + Statement last = stmt.getBody().get(stmt.getBody().size() - 1); + if (!(last instanceof BreakStatement) && !(last instanceof ContinueStatement) && + !(last instanceof ReturnStatement) && !(last instanceof ThrowStatement)) { + return stmt; + } + SequentialStatement seqStmt = new SequentialStatement(); + for (int i = 1; i < stmt.getBody().size(); ++i) { + seqStmt.getSequence().add(stmt.getBody().get(i)); + } + int count = referencedStatements.get(stmt); + ReferenceCountingVisitor refCounter = new ReferenceCountingVisitor(stmt); + switchStmt.acceptVisitor(refCounter); + if (count > refCounter.count) { + return stmt; + } + referencedStatements.put(stmt, 0); + for (SwitchClause clause : switchStmt.getClauses()) { + if (!(clause.getStatement() instanceof BreakStatement)) { + continue; + } + BreakStatement breakStmt = (BreakStatement)clause.getStatement(); + if (breakStmt.getTarget() == stmt) { + referencedStatements.put(stmt, referencedStatements.get(stmt) - 1); + Integer switchRefs = referencedStatements.get(switchStmt); + referencedStatements.put(switchStmt, (switchRefs != null ? switchRefs : 0) + 1); + breakStmt.setTarget(switchStmt); + } else if (breakStmt.getTarget() == switchStmt) { + clause.setStatement(seqStmt); + } + } + if (switchStmt.getDefaultClause() instanceof BreakStatement) { + BreakStatement breakStmt = (BreakStatement)switchStmt.getDefaultClause(); + if (breakStmt.getTarget() == stmt) { + referencedStatements.put(stmt, referencedStatements.get(stmt) - 1); + Integer switchRefs = referencedStatements.get(switchStmt); + referencedStatements.put(switchStmt, (switchRefs != null ? switchRefs : 0) + 1); + breakStmt.setTarget(switchStmt); + } else if (breakStmt.getTarget() == switchStmt) { + switchStmt.setDefaultClause(seqStmt); + } + } + return switchStmt; + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ExprOptimizer.java b/teavm-core/src/main/java/org/teavm/javascript/ExprOptimizer.java new file mode 100644 index 000000000..2674985de --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ExprOptimizer.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import org.teavm.javascript.ast.*; + +/** + * + * @author Alexey Andreev + */ +class ExprOptimizer { + public static Expr invert(Expr expr) { + if (expr instanceof UnaryExpr) { + UnaryExpr unary = (UnaryExpr)expr; + if (unary.getOperation() == UnaryOperation.NOT) { + return unary.getOperand(); + } + } else if (expr instanceof BinaryExpr) { + BinaryExpr binary = (BinaryExpr)expr; + Expr a = binary.getFirstOperand(); + Expr b = binary.getSecondOperand(); + switch (binary.getOperation()) { + case EQUALS: + return Expr.binary(BinaryOperation.NOT_EQUALS, a, b); + case NOT_EQUALS: + return Expr.binary(BinaryOperation.EQUALS, a, b); + case LESS: + return Expr.binary(BinaryOperation.GREATER_OR_EQUALS, a, b); + case LESS_OR_EQUALS: + return Expr.binary(BinaryOperation.GREATER, a, b); + case GREATER: + return Expr.binary(BinaryOperation.LESS_OR_EQUALS, a, b); + case GREATER_OR_EQUALS: + return Expr.binary(BinaryOperation.LESS, a, b); + case STRICT_EQUALS: + return Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, a, b); + case STRICT_NOT_EQUALS: + return Expr.binary(BinaryOperation.STRICT_EQUALS, a, b); + default: + break; + } + } + return Expr.invert(expr); + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java b/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java index 6e91ec780..db526a544 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java @@ -15,7 +15,16 @@ */ package org.teavm.javascript; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.*; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.teavm.codegen.DefaultAliasProvider; +import org.teavm.codegen.DefaultNamingStrategy; +import org.teavm.codegen.SourceWriter; import org.teavm.common.*; import org.teavm.javascript.ast.*; import org.teavm.model.*; @@ -201,9 +210,50 @@ public class MethodDecompiler { this.loops = loops; } - public static void main(String... args) { + public static void main(String... args) throws IOException { ClassHolderSource source = new ClassHolderSource(); - Parser parser = new Parser(); - parser.parseClass(null); + ClassHolder arrayListCls = Parser.parseClass(readClass(ArrayList.class.getName())); + source.putClassHolder(arrayListCls); + source.putClassHolder(Parser.parseClass(readClass(AbstractList.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(StringBuilder.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(IllegalArgumentException.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(IndexOutOfBoundsException.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(Exception.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(RuntimeException.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(Throwable.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(System.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(Object.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(Arrays.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(ArrayList.class.getName() + "$ListItr"))); + source.putClassHolder(Parser.parseClass(readClass(ArrayList.class.getName() + "$Itr"))); + source.putClassHolder(Parser.parseClass(readClass(ArrayList.class.getName() + "$SubList"))); + source.putClassHolder(Parser.parseClass(readClass(Collection.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(ObjectOutputStream.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(ObjectInputStream.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(ConcurrentModificationException.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(Math.class.getName()))); + source.putClassHolder(Parser.parseClass(readClass(OutOfMemoryError.class.getName()))); + MethodDecompiler decompiler = new MethodDecompiler(source); + DefaultAliasProvider aliasProvider = new DefaultAliasProvider(); + DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, source); + SourceWriter writer = new SourceWriter(naming); + Renderer renderer = new Renderer(writer, source); + Optimizer optimizer = new Optimizer(); + for (MethodHolder method : arrayListCls.getMethods()) { + RenderableMethod renderableMethod = decompiler.decompile(method); + optimizer.optimize(renderableMethod); + renderer.render(renderableMethod); + } + System.out.println(writer); + } + + private static ClassNode readClass(String className) throws IOException { + ClassLoader classLoader = MethodDecompiler.class.getClassLoader(); + try (InputStream input = classLoader.getResourceAsStream(className.replace('.', '/') + ".class")) { + ClassReader reader = new ClassReader(input); + ClassNode node = new ClassNode(); + reader.accept(node, 0); + return node; + } } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java new file mode 100644 index 000000000..3b39ad247 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import org.teavm.javascript.ast.RenderableMethod; +import org.teavm.javascript.ast.Statement; +import org.teavm.model.MethodHolder; + + +/** + * + * @author Alexey Andreev + */ +public class Optimizer { + public Statement optimize(MethodHolder method, Statement statement) { + ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getProgram().variableCount()); + statement.acceptVisitor(stats); + OptimizingVisitor optimizer = new OptimizingVisitor(stats); + statement.acceptVisitor(optimizer); + return optimizer.resultStmt; + } + + public void optimize(RenderableMethod method) { + ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariableCount()); + method.getBody().acceptVisitor(stats); + OptimizingVisitor optimizer = new OptimizingVisitor(stats); + method.getBody().acceptVisitor(optimizer); + method.setBody(optimizer.resultStmt); + int paramCount = method.getMetadata().parameterCount(); + UnusedVariableEliminator unusedEliminator = new UnusedVariableEliminator(paramCount, method.getVariableCount()); + method.getBody().acceptVisitor(unusedEliminator); + method.setVariableCount(unusedEliminator.lastIndex); + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java new file mode 100644 index 000000000..624da483f --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java @@ -0,0 +1,474 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import java.util.*; +import org.teavm.javascript.ast.*; + +/** + * + * @author Alexey Andreev + */ +class OptimizingVisitor implements StatementVisitor, ExprVisitor { + public Expr resultExpr; + public Statement resultStmt; + private ReadWriteStatsBuilder stats; + private Map copies = new HashMap<>(); + private Map referencedStatements = new HashMap<>(); + private ConditionalOptimizer conditionalOptimizer = new ConditionalOptimizer(); + private List resultSequence; + + public OptimizingVisitor(ReadWriteStatsBuilder stats) { + this.stats = stats; + conditionalOptimizer.referencedStatements = referencedStatements; + conditionalOptimizer.stats = stats; + } + + private static boolean isZero(Expr expr) { + return expr instanceof ConstantExpr && + Integer.valueOf(0).equals(((ConstantExpr)expr).getValue()); + } + + private static boolean isComparison(Expr expr) { + return expr instanceof BinaryExpr && + ((BinaryExpr)expr).getOperation() == BinaryOperation.COMPARE; + } + + @Override + public void visit(BinaryExpr expr) { + expr.getSecondOperand().acceptVisitor(this); + Expr b = resultExpr; + expr.getFirstOperand().acceptVisitor(this); + Expr a = resultExpr; + Expr p = a; + Expr q = b; + boolean invert = false; + if (isZero(p)) { + Expr tmp = p; + p = q; + q = tmp; + invert = true; + } + if (isComparison(p) && isZero(q)) { + switch (expr.getOperation()) { + case EQUALS: + case NOT_EQUALS: + case LESS: + case LESS_OR_EQUALS: + case GREATER: + case GREATER_OR_EQUALS: { + BinaryExpr comparison = (BinaryExpr)p; + Expr result = BinaryExpr.binary(expr.getOperation(), + comparison.getFirstOperand(), comparison.getSecondOperand()); + if (invert) { + result = ExprOptimizer.invert(result); + } + resultExpr = result; + return; + } + default: + break; + } + } + resultExpr = Expr.binary(expr.getOperation(), a, b); + } + + @Override + public void visit(UnaryExpr expr) { + expr.getOperand().acceptVisitor(this); + Expr operand = resultExpr; + resultExpr = Expr.unary(expr.getOperation(), operand); + } + + @Override + public void visit(ConditionalExpr expr) { + expr.getCondition().acceptVisitor(this); + Expr cond = resultExpr; + expr.getConsequent().acceptVisitor(this); + Expr consequent = resultExpr; + expr.getAlternative().acceptVisitor(this); + Expr alternative = resultExpr; + ConditionalExpr result = new ConditionalExpr(); + result.setCondition(cond); + result.setConsequent(consequent); + result.setAlternative(alternative); + resultExpr = result; + } + + @Override + public void visit(ConstantExpr expr) { + resultExpr = Expr.constant(expr.getValue()); + } + + @Override + public void visit(VariableExpr expr) { + int index = expr.getIndex(); + resultExpr = Expr.var(index); + if (stats.reads[index] != 1 || stats.writes[index] != 1) { + return; + } + if (resultSequence.isEmpty()) { + return; + } + Statement last = resultSequence.get(resultSequence.size() - 1); + if (!(last instanceof AssignmentStatement)) { + return; + } + AssignmentStatement assignment = (AssignmentStatement)last; + if (!(assignment.getLeftValue() instanceof VariableExpr)) { + return; + } + VariableExpr var = (VariableExpr)assignment.getLeftValue(); + if (var.getIndex() == index) { + resultSequence.remove(resultSequence.size() - 1); + assignment.getRightValue().acceptVisitor(this); + } + } + + @Override + public void visit(SubscriptExpr expr) { + expr.getIndex().acceptVisitor(this); + Expr index = resultExpr; + expr.getArray().acceptVisitor(this); + Expr array = resultExpr; + resultExpr = Expr.subscript(array, index); + } + + @Override + public void visit(InvocationExpr expr) { + Expr[] args = new Expr[expr.getArguments().size()]; + for (int i = expr.getArguments().size() - 1; i >= 0; --i) { + expr.getArguments().get(i).acceptVisitor(this); + args[i] = resultExpr; + } + switch (expr.getType()) { + case STATIC: + resultExpr = Expr.invokeStatic(expr.getClassName(), expr.getMethod(), args); + break; + case DYNAMIC: + resultExpr = Expr.invoke(expr.getClassName(), expr.getMethod(), args[0], Arrays.copyOfRange( + args, 1, args.length)); + break; + case SPECIAL: + resultExpr = Expr.invokeSpecial(expr.getClassName(), expr.getMethod(), args[0], Arrays.copyOfRange( + args, 1, args.length)); + break; + case CONSTRUCTOR: + resultExpr = Expr.constructObject(expr.getClassName(), expr.getMethod(), args); + break; + } + } + + private boolean tryApplyConstructor(InvocationExpr expr) { + if (!expr.getMethod().getName().equals("")) { + return false; + } + if (resultSequence == null || resultSequence.isEmpty()) { + return false; + } + Statement last = resultSequence.get(resultSequence.size() - 1); + if (!(last instanceof AssignmentStatement)) { + return false; + } + AssignmentStatement assignment = (AssignmentStatement)last; + if (!(assignment.getLeftValue() instanceof VariableExpr)) { + return false; + } + VariableExpr var = (VariableExpr)assignment.getLeftValue(); + if (!(expr.getArguments().get(0) instanceof VariableExpr)) { + return false; + } + VariableExpr target = (VariableExpr)expr.getArguments().get(0); + if (target.getIndex() != var.getIndex()) { + return false; + } + if (!(assignment.getRightValue() instanceof NewExpr)) { + return false; + } + NewExpr constructed = (NewExpr)assignment.getRightValue(); + if (!constructed.getConstructedClass().equals(expr.getClassName())) { + return false; + } + Expr[] args = expr.getArguments().toArray(new Expr[0]); + args = Arrays.copyOfRange(args, 1, args.length); + assignment.setRightValue(Expr.constructObject(expr.getClassName(), expr.getMethod(), args)); + stats.reads[var.getIndex()]--; + return true; + } + + @Override + public void visit(QualificationExpr expr) { + expr.getQualified().acceptVisitor(this); + Expr qualified = resultExpr; + resultExpr = Expr.qualify(qualified, expr.getClassName(), expr.getField()); + } + + @Override + public void visit(NewExpr expr) { + resultExpr = Expr.createObject(expr.getConstructedClass()); + } + + @Override + public void visit(NewArrayExpr expr) { + expr.getLength().acceptVisitor(this); + Expr length = resultExpr; + resultExpr = Expr.createArray(expr.getType(), length); + } + + @Override + public void visit(NewMultiArrayExpr expr) { + NewMultiArrayExpr result = new NewMultiArrayExpr(); + result.setType(expr.getType()); + for (Expr dimension : expr.getDimensions()) { + dimension.acceptVisitor(this); + result.getDimensions().add(resultExpr); + } + resultExpr = result; + } + + @Override + public void visit(InstanceOfExpr expr) { + expr.getExpr().acceptVisitor(this); + Expr value = resultExpr; + resultExpr = Expr.instanceOf(value, expr.getType()); + } + + @Override + public void visit(StaticClassExpr expr) { + resultExpr = Expr.staticClass(expr.getType()); + } + + @Override + public void visit(AssignmentStatement statement) { + if (statement.getLeftValue() == null) { + statement.getRightValue().acceptVisitor(this); + if (resultExpr instanceof InvocationExpr && + tryApplyConstructor((InvocationExpr)resultExpr)) { + resultStmt = new SequentialStatement(); + } else { + resultStmt = Statement.assign(null, resultExpr); + } + } else { + statement.getRightValue().acceptVisitor(this); + Expr right = resultExpr; + Expr left; + if (statement.getLeftValue() instanceof VariableExpr) { + VariableExpr leftVar = (VariableExpr)statement.getLeftValue(); + left = statement.getLeftValue(); + if (stats.reads[leftVar.getIndex()] == 0) { + left = null; + } else { + left = statement.getLeftValue(); + } + } else { + statement.getLeftValue().acceptVisitor(this); + left = resultExpr; + } + resultStmt = Statement.assign(left, right); + } + } + + private List processSequence(List statements, boolean strict) { + List backup = resultSequence; + List result = new ArrayList<>(); + if (strict) { + resultSequence = result; + } + for (Statement part : statements) { + part.acceptVisitor(this); + if (resultStmt != null) { + resultSequence = result; + if (resultStmt instanceof SequentialStatement) { + result.addAll(((SequentialStatement)resultStmt).getSequence()); + } else { + result.add(resultStmt); + } + } + } + resultSequence = backup; + return result; + } + + @Override + public void visit(SequentialStatement statement) { + List statements = processSequence(statement.getSequence(), false); + if (statements.isEmpty()) { + resultStmt = null; + return; + } + if (statements.size() == 1) { + resultStmt = statements.get(0); + } else { + SequentialStatement result = new SequentialStatement(); + result.getSequence().addAll(statements); + resultStmt = result; + } + } + + @Override + public void visit(ConditionalStatement statement) { + statement.getCondition().acceptVisitor(this); + Expr predicate = resultExpr; + List sequenceBackup = resultSequence; + resultSequence = new ArrayList<>(); + statement.getConsequent().acceptVisitor(this); + Statement consequent = resultStmt; + Statement alternative = null; + if (statement.getAlternative() != null) { + statement.getAlternative().acceptVisitor(this); + alternative = resultStmt; + } + if (consequent == null) { + if (alternative != null) { + Statement tmp = alternative; + alternative = consequent; + consequent = tmp; + predicate = ExprOptimizer.invert(predicate); + } else { + consequent = Statement.empty(); + } + } + resultStmt = conditionalOptimizer.tryMakeInline( + (ConditionalStatement)Statement.cond(predicate, consequent, alternative)); + resultSequence = sequenceBackup; + } + + private void visitIdentified(IdentifiedStatement stmt, IdentifiedStatement copy) { + copies.put(stmt, copy); + } + + @Override + public void visit(SwitchStatement statement) { + SwitchStatement result = new SwitchStatement(); + result.setId(statement.getId()); + visitIdentified(statement, result); + statement.getValue().acceptVisitor(this); + result.setValue(resultExpr); + for (SwitchClause clause : statement.getClauses()) { + clause.getStatement().acceptVisitor(this); + SwitchClause resultClause = new SwitchClause(); + resultClause.setConditions(clause.getConditions()); + resultClause.setStatement(resultStmt != null ? resultStmt : Statement.empty()); + result.getClauses().add(resultClause); + } + if (statement.getDefaultClause() != null) { + statement.getDefaultClause().acceptVisitor(this); + } else { + resultStmt = null; + } + result.setDefaultClause(resultStmt != null ? resultStmt : Statement.empty()); + resultStmt = result; + } + + @Override + public void visit(WhileStatement statement) { + WhileStatement result = new WhileStatement(); + result.setId(statement.getId()); + visitIdentified(statement, result); + List statements = processSequence(statement.getBody(), false); + if (statement.getCondition() != null) { + statement.getCondition().acceptVisitor(this); + result.setCondition(resultExpr); + } else { + result.setCondition(null); + } + result.getBody().addAll(statements); + conditionalOptimizer.tryOptimize(result); + resultStmt = result; + } + + @Override + public void visit(BlockStatement statement) { + BlockStatement result = new BlockStatement(); + result.setId(statement.getId()); + visitIdentified(statement, result); + List statements = processSequence(statement.getBody(), false); + result.getBody().addAll(statements); + if (referencedStatements.containsKey(result)) { + resultStmt = conditionalOptimizer.tryOptimize(result); + } else { + SequentialStatement altResult = new SequentialStatement(); + altResult.getSequence().addAll(result.getBody()); + resultStmt = altResult; + } + if (resultStmt instanceof BlockStatement) { + resultStmt = conditionalOptimizer.tryOptimizeElse((BlockStatement)resultStmt); + } + if (resultStmt instanceof BlockStatement) { + resultStmt = conditionalOptimizer.tryOptimizeSwitch((BlockStatement)resultStmt); + } + if (resultStmt instanceof ConditionalStatement) { + ConditionalStatement conditional = (ConditionalStatement)resultStmt; + conditional.getCondition().acceptVisitor(this); + conditional.setCondition(resultExpr); + } + } + + @Override + public void visit(ForStatement statement) { + } + + @Override + public void visit(BreakStatement statement) { + BreakStatement result = new BreakStatement(); + if (statement.getTarget() != null) { + IdentifiedStatement targetCopy = copies.get(statement.getTarget()); + result.setTarget(targetCopy); + Integer refCount = referencedStatements.get(targetCopy); + if (refCount == null) { + refCount = 0; + } + referencedStatements.put(targetCopy, refCount + 1); + } + resultStmt = result; + } + + @Override + public void visit(ContinueStatement statement) { + ContinueStatement result = new ContinueStatement(); + if (statement.getTarget() != null) { + IdentifiedStatement targetCopy = copies.get(statement.getTarget()); + result.setTarget(targetCopy); + Integer refCount = referencedStatements.get(targetCopy); + if (refCount == null) { + refCount = 0; + } + referencedStatements.put(targetCopy, refCount + 1); + } + resultStmt = result; + } + + @Override + public void visit(ReturnStatement statement) { + if (statement.getResult() == null) { + resultStmt = Statement.exitFunction(null); + } else { + statement.getResult().acceptVisitor(this); + resultStmt = Statement.exitFunction(resultExpr); + } + } + + @Override + public void visit(ThrowStatement statement) { + statement.getException().acceptVisitor(this); + resultStmt = Statement.raiseException(resultExpr); + } + + @Override + public void visit(IncrementStatement statement) { + resultStmt = Statement.increment(statement.getVar(), statement.getAmount()); + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java new file mode 100644 index 000000000..235d84e9c --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java @@ -0,0 +1,189 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import org.teavm.javascript.ast.*; + +/** + * + * @author Alexey Andreev + */ +class ReadWriteStatsBuilder implements StatementVisitor, ExprVisitor { + public int[] reads; + public int[] writes; + + public ReadWriteStatsBuilder(int variableCount) { + reads = new int[variableCount]; + writes = new int[variableCount]; + } + + @Override + public void visit(BinaryExpr expr) { + expr.getFirstOperand().acceptVisitor(this); + expr.getSecondOperand().acceptVisitor(this); + } + + @Override + public void visit(UnaryExpr expr) { + expr.getOperand().acceptVisitor(this); + } + + @Override + public void visit(ConditionalExpr expr) { + expr.getCondition().acceptVisitor(this); + expr.getConsequent().acceptVisitor(this); + expr.getAlternative().acceptVisitor(this); + } + + @Override + public void visit(ConstantExpr expr) { + } + + @Override + public void visit(VariableExpr expr) { + reads[expr.getIndex()]++; + } + + @Override + public void visit(SubscriptExpr expr) { + expr.getArray().acceptVisitor(this); + expr.getIndex().acceptVisitor(this); + } + + @Override + public void visit(InvocationExpr expr) { + for (Expr arg : expr.getArguments()) { + arg.acceptVisitor(this); + } + } + + @Override + public void visit(QualificationExpr expr) { + expr.getQualified().acceptVisitor(this); + } + + @Override + public void visit(NewExpr expr) { + } + + @Override + public void visit(NewArrayExpr expr) { + expr.getLength().acceptVisitor(this); + } + + @Override + public void visit(NewMultiArrayExpr expr) { + for (Expr dimension : expr.getDimensions()) { + dimension.acceptVisitor(this); + } + } + + @Override + public void visit(InstanceOfExpr expr) { + expr.getExpr().acceptVisitor(this); + } + + @Override + public void visit(StaticClassExpr expr) { + } + + @Override + public void visit(AssignmentStatement statement) { + if (statement.getLeftValue() != null) { + if (statement.getLeftValue() instanceof VariableExpr) { + VariableExpr leftVar = (VariableExpr)statement.getLeftValue(); + writes[leftVar.getIndex()]++; + } else { + statement.getLeftValue().acceptVisitor(this); + } + } + statement.getRightValue().acceptVisitor(this); + } + + @Override + public void visit(SequentialStatement statement) { + for (Statement part : statement.getSequence()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(ConditionalStatement statement) { + statement.getCondition().acceptVisitor(this); + statement.getConsequent().acceptVisitor(this); + if (statement.getAlternative() != null) { + statement.getAlternative().acceptVisitor(this); + } + } + + @Override + public void visit(SwitchStatement statement) { + statement.getValue().acceptVisitor(this); + for (SwitchClause clause : statement.getClauses()) { + clause.getStatement().acceptVisitor(this); + } + if (statement.getDefaultClause() != null) { + statement.getDefaultClause().acceptVisitor(this); + } + } + + @Override + public void visit(WhileStatement statement) { + if (statement.getCondition() != null) { + statement.getCondition().acceptVisitor(this); + } + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(BlockStatement statement) { + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(ForStatement statement) { + } + + @Override + public void visit(BreakStatement statement) { + } + + @Override + public void visit(ContinueStatement statement) { + } + + @Override + public void visit(ReturnStatement statement) { + if (statement.getResult() != null) { + statement.getResult().acceptVisitor(this); + } + } + + @Override + public void visit(ThrowStatement statement) { + statement.getException().acceptVisitor(this); + } + + @Override + public void visit(IncrementStatement statement) { + reads[statement.getVar()]++; + writes[statement.getVar()]++; + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java new file mode 100644 index 000000000..679997c87 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ReferenceCountingVisitor.java @@ -0,0 +1,103 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import org.teavm.javascript.ast.*; + +/** + * + * @author Alexey Andreev + */ +class ReferenceCountingVisitor implements StatementVisitor { + private IdentifiedStatement target; + public int count; + + public ReferenceCountingVisitor(IdentifiedStatement target) { + this.target = target; + } + + @Override + public void visit(AssignmentStatement statement) { + } + + @Override + public void visit(SequentialStatement statement) { + for (Statement part : statement.getSequence()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(ConditionalStatement statement) { + statement.getConsequent().acceptVisitor(this); + if (statement.getAlternative() != null) { + statement.getAlternative().acceptVisitor(this); + } + } + + @Override + public void visit(SwitchStatement statement) { + for (SwitchClause clause : statement.getClauses()) { + clause.getStatement().acceptVisitor(this); + } + statement.getDefaultClause().acceptVisitor(this); + } + + @Override + public void visit(WhileStatement statement) { + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(BlockStatement statement) { + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(ForStatement statement) { + } + + @Override + public void visit(BreakStatement statement) { + if (statement.getTarget() == target) { + ++count; + } + } + + @Override + public void visit(ContinueStatement statement) { + if (statement.getTarget() == target) { + ++count; + } + } + + @Override + public void visit(ReturnStatement statement) { + } + + @Override + public void visit(ThrowStatement statement) { + } + + @Override + public void visit(IncrementStatement statement) { + } + +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index e15fb953a..260abd5fc 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -210,7 +210,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { first = false; writer.append(variableName(i)); } - writer.append(";\n"); + writer.append(";").newLine(); } method.getBody().acceptVisitor(this); writer.outdent().append("}").newLine(); @@ -240,7 +240,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.append(") {").newLine().indent(); statement.getConsequent().acceptVisitor(this); if (statement.getAlternative() != null) { - writer.outdent().append("} else {\n").indent().newLine(); + writer.outdent().append("} else {").indent().newLine(); statement.getAlternative().acceptVisitor(this); } writer.outdent().append("}").newLine(); @@ -354,9 +354,9 @@ public class Renderer implements ExprVisitor, StatementVisitor { } --index; if (index < variableNames.length()) { - return String.valueOf(variableNames.charAt(index)); + return Character.toString(variableNames.charAt(index)); } else { - return String.valueOf(variableNames.charAt(index % variableNames.length())) + + return Character.toString(variableNames.charAt(index % variableNames.length())) + index / variableNames.length(); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java new file mode 100644 index 000000000..18e5fd88c --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/UnusedVariableEliminator.java @@ -0,0 +1,194 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import java.util.Arrays; +import org.teavm.javascript.ast.*; + +/** + * + * @author Alexey Andreev + */ +class UnusedVariableEliminator implements ExprVisitor, StatementVisitor { + int[] indexes; + int lastIndex; + + public UnusedVariableEliminator(int parameterCount, int variableCount) { + indexes = new int[variableCount]; + Arrays.fill(indexes, -1); + for (int i = 0; i <= parameterCount; ++i) { + indexes[i] = lastIndex++; + } + } + + @Override + public void visit(AssignmentStatement statement) { + if (statement.getLeftValue() != null) { + statement.getLeftValue().acceptVisitor(this); + } + statement.getRightValue().acceptVisitor(this); + } + + @Override + public void visit(SequentialStatement statement) { + for (Statement part : statement.getSequence()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(ConditionalStatement statement) { + statement.getCondition().acceptVisitor(this); + statement.getConsequent().acceptVisitor(this); + if (statement.getAlternative() != null) { + statement.getAlternative().acceptVisitor(this); + } + } + + @Override + public void visit(SwitchStatement statement) { + statement.getValue().acceptVisitor(this); + for (SwitchClause clause : statement.getClauses()) { + clause.getStatement().acceptVisitor(this); + } + statement.getDefaultClause().acceptVisitor(this); + } + + @Override + public void visit(WhileStatement statement) { + if (statement.getCondition() != null) { + statement.getCondition().acceptVisitor(this); + } + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(BlockStatement statement) { + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(ForStatement statement) { + } + + @Override + public void visit(BreakStatement statement) { + } + + @Override + public void visit(ContinueStatement statement) { + } + + @Override + public void visit(ReturnStatement statement) { + if (statement.getResult() != null) { + statement.getResult().acceptVisitor(this); + } + } + + @Override + public void visit(ThrowStatement statement) { + statement.getException().acceptVisitor(this); + } + + private int renumber(int var) { + int index = indexes[var]; + if (index == -1) { + index = lastIndex++; + indexes[var] = index; + } + return index; + } + + @Override + public void visit(IncrementStatement statement) { + statement.setVar(renumber(statement.getVar())); + } + + @Override + public void visit(BinaryExpr expr) { + expr.getFirstOperand().acceptVisitor(this); + expr.getSecondOperand().acceptVisitor(this); + } + + @Override + public void visit(UnaryExpr expr) { + expr.getOperand().acceptVisitor(this); + } + + @Override + public void visit(ConditionalExpr expr) { + expr.getCondition().acceptVisitor(this); + expr.getConsequent().acceptVisitor(this); + expr.getAlternative().acceptVisitor(this); + } + + @Override + public void visit(ConstantExpr expr) { + } + + @Override + public void visit(VariableExpr expr) { + expr.setIndex(renumber(expr.getIndex())); + } + + @Override + public void visit(SubscriptExpr expr) { + expr.getArray().acceptVisitor(this); + expr.getIndex().acceptVisitor(this); + } + + @Override + public void visit(InvocationExpr expr) { + for (Expr arg : expr.getArguments()) { + arg.acceptVisitor(this); + } + } + + @Override + public void visit(QualificationExpr expr) { + expr.getQualified().acceptVisitor(this); + } + + @Override + public void visit(NewExpr expr) { + } + + @Override + public void visit(NewArrayExpr expr) { + expr.getLength().acceptVisitor(this); + } + + @Override + public void visit(NewMultiArrayExpr expr) { + for (Expr dimension : expr.getDimensions()) { + dimension.acceptVisitor(this); + } + } + + @Override + public void visit(InstanceOfExpr expr) { + expr.getExpr().acceptVisitor(this); + } + + @Override + public void visit(StaticClassExpr expr) { + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/Program.java b/teavm-core/src/main/java/org/teavm/model/Program.java index 50e1828a6..524d163f0 100644 --- a/teavm-core/src/main/java/org/teavm/model/Program.java +++ b/teavm-core/src/main/java/org/teavm/model/Program.java @@ -67,6 +67,7 @@ public class Program { for (int i = 0; i < basicBlocks.size(); ++i) { BasicBlock block = basicBlocks.get(i); if (block != null) { + block.setIndex(sz); basicBlocks.set(sz++, block); } } 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 60a2d15af..76a3ad89b 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -300,7 +300,7 @@ public class ProgramParser { insn.setCondition(getVariable(--currentDepth)); insn.getEntries().addAll(Arrays.asList(table)); builder.add(insn); - int defaultIndex = labelIndexes.get(insn); + int defaultIndex = labelIndexes.get(dflt); insn.setDefaultTarget(getBasicBlock(defaultIndex)); nextIndexes[labels.length] = defaultIndex; } diff --git a/teavm-core/src/main/resources/.gitignore b/teavm-core/src/main/resources/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/teavm-core/src/test/java/.gitignore b/teavm-core/src/test/java/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/teavm-core/src/test/resources/.gitignore b/teavm-core/src/test/resources/.gitignore new file mode 100644 index 000000000..e69de29bb