Adds JavaScript AST optimizer.

This commit is contained in:
konsoletyper 2013-10-01 21:45:19 +04:00
parent 3445910866
commit 4e4d0cfe9e
16 changed files with 1374 additions and 14 deletions

View File

@ -16,6 +16,10 @@ public class SourceWriter {
this.naming = naming; this.naming = naming;
} }
public void clear() {
sb.setLength(0);
}
public SourceWriter append(String value) { public SourceWriter append(String value) {
appendIndent(); appendIndent();
sb.append(value); sb.append(value);
@ -34,6 +38,12 @@ public class SourceWriter {
return this; return this;
} }
public SourceWriter append(char value) {
appendIndent();
sb.append(value);
return this;
}
public SourceWriter appendClass(String cls) { public SourceWriter appendClass(String cls) {
appendIndent(); appendIndent();
sb.append(naming.getNameFor(cls)); sb.append(naming.getNameFor(cls));
@ -69,7 +79,6 @@ public class SourceWriter {
public SourceWriter indent() { public SourceWriter indent() {
++indentSize; ++indentSize;
newLine();
return this; return this;
} }

View File

@ -12,19 +12,23 @@ import java.util.List;
public class GraphBuilder { public class GraphBuilder {
private GraphImpl builtGraph; private GraphImpl builtGraph;
private List<IntegerArray> addedEdges = new ArrayList<>(); private List<IntegerArray> addedEdges = new ArrayList<>();
private int sz = 0;
public GraphBuilder() { public GraphBuilder() {
} }
public GraphBuilder(int sz) { public GraphBuilder(int sz) {
addedEdges.addAll(Collections.<IntegerArray>nCopies(sz, null)); addedEdges.addAll(Collections.<IntegerArray>nCopies(sz, null));
this.sz = sz;
} }
public void clear() { public void clear() {
addedEdges.clear(); addedEdges.clear();
sz = 0;
} }
public void addEdge(int from, int to) { public void addEdge(int from, int to) {
sz = Math.max(sz, Math.max(from, to) + 1);
builtGraph = null; builtGraph = null;
if (addedEdges.size() == from) { if (addedEdges.size() == from) {
addedEdges.add(IntegerArray.of(to)); addedEdges.add(IntegerArray.of(to));
@ -43,11 +47,11 @@ public class GraphBuilder {
public Graph build() { public Graph build() {
if (builtGraph == null) { if (builtGraph == null) {
IntegerArray[] incomingEdges = new IntegerArray[addedEdges.size()]; IntegerArray[] incomingEdges = new IntegerArray[sz];
for (int i = 0; i < addedEdges.size(); ++i) { for (int i = 0; i < sz; ++i) {
incomingEdges[i] = new IntegerArray(1); incomingEdges[i] = new IntegerArray(1);
} }
int[][] outgoingEdgeList = new int[addedEdges.size()][]; int[][] outgoingEdgeList = new int[sz][];
for (int i = 0; i < addedEdges.size(); ++i) { for (int i = 0; i < addedEdges.size(); ++i) {
IntegerArray edgeList = addedEdges.get(i); IntegerArray edgeList = addedEdges.get(i);
outgoingEdgeList[i] = edgeList != null ? edgeList.getAll() : new int[0]; outgoingEdgeList[i] = edgeList != null ? edgeList.getAll() : new int[0];
@ -55,8 +59,11 @@ public class GraphBuilder {
incomingEdges[j].add(i); incomingEdges[j].add(i);
} }
} }
int[][] incomingEdgeList = new int[addedEdges.size()][]; for (int i = addedEdges.size(); i < sz; ++i) {
for (int i = 0; i < addedEdges.size(); ++i) { outgoingEdgeList[i] = new int[0];
}
int[][] incomingEdgeList = new int[sz][];
for (int i = 0; i < sz; ++i) {
incomingEdgeList[i] = incomingEdges[i].getAll(); incomingEdgeList[i] = incomingEdges[i].getAll();
} }
builtGraph = new GraphImpl(incomingEdgeList, outgoingEdgeList); builtGraph = new GraphImpl(incomingEdgeList, outgoingEdgeList);

View File

@ -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<IdentifiedStatement, Integer> 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;
}
}

View File

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

View File

@ -15,7 +15,16 @@
*/ */
package org.teavm.javascript; package org.teavm.javascript;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*; 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.common.*;
import org.teavm.javascript.ast.*; import org.teavm.javascript.ast.*;
import org.teavm.model.*; import org.teavm.model.*;
@ -201,9 +210,50 @@ public class MethodDecompiler {
this.loops = loops; this.loops = loops;
} }
public static void main(String... args) { public static void main(String... args) throws IOException {
ClassHolderSource source = new ClassHolderSource(); ClassHolderSource source = new ClassHolderSource();
Parser parser = new Parser(); ClassHolder arrayListCls = Parser.parseClass(readClass(ArrayList.class.getName()));
parser.parseClass(null); 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;
}
} }
} }

View File

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

View File

@ -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<IdentifiedStatement, IdentifiedStatement> copies = new HashMap<>();
private Map<IdentifiedStatement, Integer> referencedStatements = new HashMap<>();
private ConditionalOptimizer conditionalOptimizer = new ConditionalOptimizer();
private List<Statement> 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("<init>")) {
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<Statement> processSequence(List<Statement> statements, boolean strict) {
List<Statement> backup = resultSequence;
List<Statement> 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<Statement> 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<Statement> 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<Statement> 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<Statement> 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());
}
}

View File

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

View File

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

View File

@ -210,7 +210,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
first = false; first = false;
writer.append(variableName(i)); writer.append(variableName(i));
} }
writer.append(";\n"); writer.append(";").newLine();
} }
method.getBody().acceptVisitor(this); method.getBody().acceptVisitor(this);
writer.outdent().append("}").newLine(); writer.outdent().append("}").newLine();
@ -240,7 +240,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
writer.append(") {").newLine().indent(); writer.append(") {").newLine().indent();
statement.getConsequent().acceptVisitor(this); statement.getConsequent().acceptVisitor(this);
if (statement.getAlternative() != null) { if (statement.getAlternative() != null) {
writer.outdent().append("} else {\n").indent().newLine(); writer.outdent().append("} else {").indent().newLine();
statement.getAlternative().acceptVisitor(this); statement.getAlternative().acceptVisitor(this);
} }
writer.outdent().append("}").newLine(); writer.outdent().append("}").newLine();
@ -354,9 +354,9 @@ public class Renderer implements ExprVisitor, StatementVisitor {
} }
--index; --index;
if (index < variableNames.length()) { if (index < variableNames.length()) {
return String.valueOf(variableNames.charAt(index)); return Character.toString(variableNames.charAt(index));
} else { } else {
return String.valueOf(variableNames.charAt(index % variableNames.length())) + return Character.toString(variableNames.charAt(index % variableNames.length())) +
index / variableNames.length(); index / variableNames.length();
} }
} }

View File

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

View File

@ -67,6 +67,7 @@ public class Program {
for (int i = 0; i < basicBlocks.size(); ++i) { for (int i = 0; i < basicBlocks.size(); ++i) {
BasicBlock block = basicBlocks.get(i); BasicBlock block = basicBlocks.get(i);
if (block != null) { if (block != null) {
block.setIndex(sz);
basicBlocks.set(sz++, block); basicBlocks.set(sz++, block);
} }
} }

View File

@ -300,7 +300,7 @@ public class ProgramParser {
insn.setCondition(getVariable(--currentDepth)); insn.setCondition(getVariable(--currentDepth));
insn.getEntries().addAll(Arrays.asList(table)); insn.getEntries().addAll(Arrays.asList(table));
builder.add(insn); builder.add(insn);
int defaultIndex = labelIndexes.get(insn); int defaultIndex = labelIndexes.get(dflt);
insn.setDefaultTarget(getBasicBlock(defaultIndex)); insn.setDefaultTarget(getBasicBlock(defaultIndex));
nextIndexes[labels.length] = defaultIndex; nextIndexes[labels.length] = defaultIndex;
} }

View File

0
teavm-core/src/test/java/.gitignore vendored Normal file
View File

View File