From ce0859beac3e70aa5e0fb1fde72e95b217edfb76 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Fri, 31 Jan 2014 14:03:29 +0400 Subject: [PATCH] AST optimizations and output code minification --- .../java/org/teavm/javascript/Decompiler.java | 4 + .../java/org/teavm/javascript/Optimizer.java | 2 + .../teavm/javascript/OptimizingVisitor.java | 121 +++++++++++------ .../javascript/RedundantLabelEliminator.java | 122 ++++++++++++++++++ .../java/org/teavm/javascript/Renderer.java | 82 +++++++----- .../org/teavm/javascript/ast/ClassNode.java | 7 + .../teavm/javascript/ast/NodeModifier.java | 3 +- 7 files changed, 266 insertions(+), 75 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index 220b395d2..a0d07089e 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -120,6 +120,7 @@ public class Decompiler { clsNode.getMethods().add(decompile(method)); } clsNode.getInterfaces().addAll(cls.getInterfaces()); + clsNode.getModifiers().addAll(mapModifiers(cls.getModifiers())); return clsNode; } @@ -213,6 +214,7 @@ public class Decompiler { } Optimizer optimizer = new Optimizer(); optimizer.optimize(methodNode, method.getProgram()); + methodNode.getModifiers().addAll(mapModifiers(method.getModifiers())); return methodNode; } @@ -220,6 +222,8 @@ public class Decompiler { Set result = EnumSet.noneOf(NodeModifier.class); if (modifiers.contains(ElementModifier.STATIC)) { result.add(NodeModifier.STATIC); + } else if (modifiers.contains(ElementModifier.INTERFACE)) { + result.add(NodeModifier.INTERFACE); } return result; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java index ea374db8f..f107401c2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java @@ -37,6 +37,8 @@ public class Optimizer { UnusedVariableEliminator unusedEliminator = new UnusedVariableEliminator(paramCount, method.getVariables()); method.getBody().acceptVisitor(unusedEliminator); method.getVariables().subList(unusedEliminator.lastIndex, method.getVariables().size()).clear(); + RedundantLabelEliminator labelEliminator = new RedundantLabelEliminator(); + method.getBody().acceptVisitor(labelEliminator); for (int i = 0; i < method.getVariables().size(); ++i) { method.getVariables().set(i, i); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java index 3d88cb72d..b49e08c82 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java +++ b/teavm-core/src/main/java/org/teavm/javascript/OptimizingVisitor.java @@ -28,6 +28,10 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { private ReadWriteStatsBuilder stats; Map referencedStatements = new HashMap<>(); private List resultSequence; + private int[] variableDecl; + private List invocations; + private int lastMethodCall; + private boolean hasMethodCall; public OptimizingVisitor(ReadWriteStatsBuilder stats) { this.stats = stats; @@ -118,22 +122,27 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { if (stats.reads[index] != 1 || stats.writes[index] != 1) { return; } - if (resultSequence.isEmpty()) { + int declIndex = variableDecl[index]; + if (declIndex < 0 || declIndex >= lastMethodCall) { return; } - Statement last = resultSequence.get(resultSequence.size() - 1); - if (!(last instanceof AssignmentStatement)) { - return; + if (invocations.get(declIndex)) { + for (int i = lastMethodCall - 1; i > declIndex; --i) { + if (invocations.get(i)) { + 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); + for (int i = declIndex; i < lastMethodCall; ++i) { + if (invocations.get(i)) { + lastMethodCall = i; + break; + } } + AssignmentStatement decl = (AssignmentStatement)resultSequence.get(declIndex); + resultSequence.set(declIndex, null); + variableDecl[index] = -1; + resultExpr = decl.getRightValue(); } @Override @@ -157,6 +166,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(InvocationExpr expr) { + hasMethodCall = true; Expr[] args = new Expr[expr.getArguments().size()]; for (int i = expr.getArguments().size() - 1; i >= 0; --i) { expr.getArguments().get(i).acceptVisitor(this); @@ -266,6 +276,9 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { if (!(statement.getLeftValue() instanceof VariableExpr)) { statement.getLeftValue().acceptVisitor(this); left = resultExpr; + } else { + VariableExpr var = (VariableExpr)statement.getLeftValue(); + variableDecl[var.getIndex()] = resultSequence.size(); } statement.setLeftValue(left); statement.setRightValue(right); @@ -273,34 +286,57 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { } } - private List processSequence(List statements, boolean strict) { + private List processSequence(List statements) { + boolean hasMethodCallBackup = hasMethodCall; List backup = resultSequence; + int[] variableDeclBackup = variableDecl; + List invocationsBackup = invocations; + invocations = new ArrayList<>(); + resultSequence = new ArrayList<>(); + variableDecl = new int[stats.reads.length]; + Arrays.fill(variableDecl, -1); + processSequenceImpl(statements); List result = new ArrayList<>(); - if (strict) { - resultSequence = result; - } - outer: for (int i = 0; i < statements.size(); ++i) { - Statement part = statements.get(i); - part.acceptVisitor(this); - List newStatements = new ArrayList<>(); - if (resultStmt instanceof SequentialStatement) { - newStatements.addAll(((SequentialStatement)resultStmt).getSequence()); - } else { - newStatements.add(resultStmt); - } - for (int j = 0; j < newStatements.size(); ++j) { - Statement newStatement = newStatements.get(j); - resultSequence = result; - result.add(newStatement); - if (newStatement instanceof BreakStatement) { - break outer; - } + for (Statement part : resultSequence) { + if (part != null) { + result.add(part); } } resultSequence = backup; + variableDecl = variableDeclBackup; + invocations = invocationsBackup; + hasMethodCall = hasMethodCallBackup; return result; } + private boolean processSequenceImpl(List statements) { + for (int i = 0; i < statements.size(); ++i) { + Statement part = statements.get(i); + if (part instanceof SequentialStatement) { + if (!processSequenceImpl(((SequentialStatement)part).getSequence())) { + return false; + } + continue; + } + hasMethodCall = false; + lastMethodCall = resultSequence.size(); + part.acceptVisitor(this); + invocations.add(hasMethodCall); + part = resultStmt; + if (part instanceof SequentialStatement) { + if (!processSequenceImpl(((SequentialStatement)part).getSequence())) { + return false; + } + continue; + } + resultSequence.add(part); + if (part instanceof BreakStatement) { + return false; + } + } + return true; + } + private void eliminateRedundantBreaks(List statements, IdentifiedStatement exit) { if (statements.isEmpty()) { return; @@ -398,7 +434,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(SequentialStatement statement) { - List statements = processSequence(statement.getSequence(), false); + List statements = processSequence(statement.getSequence()); if (statements.size() == 1) { resultStmt = statements.get(0); } else { @@ -412,8 +448,8 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { public void visit(ConditionalStatement statement) { statement.getCondition().acceptVisitor(this); statement.setCondition(resultExpr); - List consequent = processSequence(statement.getConsequent(), true); - List alternative = processSequence(statement.getAlternative(), true); + List consequent = processSequence(statement.getConsequent()); + List alternative = processSequence(statement.getAlternative()); if (consequent.isEmpty()) { consequent.addAll(alternative); alternative.clear(); @@ -428,6 +464,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { statement.getAlternative().clear(); statement.getAlternative().addAll(alternative); resultStmt = statement; + hasMethodCall = true; } @Override @@ -435,14 +472,15 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { statement.getValue().acceptVisitor(this); statement.setValue(resultExpr); for (SwitchClause clause : statement.getClauses()) { - List newBody = processSequence(clause.getBody(), true); + List newBody = processSequence(clause.getBody()); clause.getBody().clear(); clause.getBody().addAll(newBody); } - List newDefault = processSequence(statement.getDefaultClause(), true); + List newDefault = processSequence(statement.getDefaultClause()); statement.getDefaultClause().clear(); statement.getDefaultClause().addAll(newDefault); resultStmt = statement; + hasMethodCall = true; } @Override @@ -454,7 +492,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { statement.getBody().clear(); statement.getBody().addAll(innerLoop.getBody()); } - List statements = processSequence(statement.getBody(), true); + List statements = processSequence(statement.getBody()); statement.getBody().clear(); statement.getBody().addAll(statements); if (statement.getCondition() != null) { @@ -481,11 +519,12 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { break; } resultStmt = statement; + hasMethodCall = true; } @Override public void visit(BlockStatement statement) { - List statements = processSequence(statement.getBody(), false); + List statements = processSequence(statement.getBody()); eliminateRedundantBreaks(statements, statement); if (referencedStatements.get(statement).equals(0)) { SequentialStatement result = new SequentialStatement(); @@ -505,11 +544,13 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(BreakStatement statement) { resultStmt = statement; + hasMethodCall = true; } @Override public void visit(ContinueStatement statement) { resultStmt = statement; + hasMethodCall = true; } @Override @@ -519,6 +560,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { statement.setResult(resultExpr); } resultStmt = statement; + hasMethodCall = true; } @Override @@ -526,6 +568,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { statement.getException().acceptVisitor(this); statement.setException(resultExpr); resultStmt = statement; + hasMethodCall = true; } @Override diff --git a/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java b/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java new file mode 100644 index 000000000..235d8f1e4 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/RedundantLabelEliminator.java @@ -0,0 +1,122 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.javascript; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.teavm.javascript.ast.*; + +/** + * + * @author Alexey Andreev + */ +class RedundantLabelEliminator implements StatementVisitor { + private IdentifiedStatement currentBlock; + private Set hasRefs = new HashSet<>(); + + void visitSequence(List statements) { + for (Statement statement : statements) { + statement.acceptVisitor(this); + } + } + + @Override + public void visit(AssignmentStatement statement) { + } + + @Override + public void visit(SequentialStatement statement) { + visitSequence(statement.getSequence()); + } + + @Override + public void visit(ConditionalStatement statement) { + visitSequence(statement.getConsequent()); + visitSequence(statement.getAlternative()); + } + + @Override + public void visit(SwitchStatement statement) { + IdentifiedStatement currentBlockBackup = currentBlock; + currentBlock = statement; + for (SwitchClause clause : statement.getClauses()) { + visitSequence(clause.getBody()); + } + visitSequence(statement.getDefaultClause()); + if (!hasRefs.contains(currentBlock)) { + currentBlock.setId(null); + } + currentBlock = currentBlockBackup; + } + + @Override + public void visit(WhileStatement statement) { + IdentifiedStatement currentBlockBackup = currentBlock; + currentBlock = statement; + visitSequence(statement.getBody()); + if (!hasRefs.contains(currentBlock)) { + currentBlock.setId(null); + } + currentBlock = currentBlockBackup; + } + + @Override + public void visit(BlockStatement statement) { + IdentifiedStatement currentBlockBackup = currentBlock; + currentBlock = null; + visitSequence(statement.getBody()); + currentBlock = currentBlockBackup; + } + + @Override + public void visit(ForStatement statement) { + } + + @Override + public void visit(BreakStatement statement) { + if (statement.getTarget() == currentBlock) { + statement.setTarget(null); + } else { + hasRefs.add(statement.getTarget()); + } + } + + @Override + public void visit(ContinueStatement statement) { + if (statement.getTarget() == currentBlock) { + statement.setTarget(null); + } else { + hasRefs.add(statement.getTarget()); + } + } + + @Override + public void visit(ReturnStatement statement) { + } + + @Override + public void visit(ThrowStatement statement) { + } + + @Override + public void visit(IncrementStatement statement) { + } + + @Override + public void visit(InitClassStatement 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 52ea6cf89..56069f761 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -150,11 +150,13 @@ public class Renderer implements ExprVisitor, StatementVisitor { .append(constantToString(value)).append(";").softNewLine(); } - writer.appendClass(cls.getName()).append(".prototype").ws().append("=").ws().append("new ") - .append(cls.getParentName() != null ? naming.getNameFor(cls.getParentName()) : - "Object").append("();").softNewLine(); - writer.appendClass(cls.getName()).append(".prototype.constructor").ws().append("=").ws() - .appendClass(cls.getName()).append(';').softNewLine(); + if (!cls.getModifiers().contains(NodeModifier.INTERFACE)) { + writer.appendClass(cls.getName()).append(".prototype").ws().append("=").ws().append("new ") + .append(cls.getParentName() != null ? naming.getNameFor(cls.getParentName()) : + "Object").append("();").softNewLine(); + writer.appendClass(cls.getName()).append(".prototype.constructor").ws().append("=").ws() + .appendClass(cls.getName()).append(';').softNewLine(); + } writer.appendClass(cls.getName()).append(".$meta").ws().append("=").ws().append("{").ws(); writer.append("name").ws().append(":").ws().append("\"").append(cls.getName()).append("\",").ws(); writer.append("primitive").ws().append(":").ws().append("false,").ws(); @@ -173,37 +175,39 @@ public class Renderer implements ExprVisitor, StatementVisitor { } writer.append("]"); writer.ws().append("};").softNewLine(); - writer.appendClass(cls.getName()).append("_$clinit").ws().append("=").ws().append("function()").ws() - .append("{").softNewLine().indent(); - writer.appendClass(cls.getName()).append("_$clinit").ws().append("=").ws() - .append("function(){};").newLine(); - List stubNames = new ArrayList<>(); - for (MethodNode method : cls.getMethods()) { - renderBody(method); - stubNames.add(naming.getFullNameFor(method.getReference())); - } - MethodHolder methodHolder = classSource.getClassHolder(cls.getName()).getMethod( - new MethodDescriptor("", ValueType.VOID)); - if (methodHolder != null) { - writer.appendMethodBody(new MethodReference(cls.getName(), methodHolder.getDescriptor())) - .append("();").softNewLine(); - } - writer.outdent().append("}").newLine(); - for (MethodNode method : cls.getMethods()) { - if (!method.getModifiers().contains(NodeModifier.STATIC)) { - renderDeclaration(method); + if (!cls.getModifiers().contains(NodeModifier.INTERFACE)) { + writer.appendClass(cls.getName()).append("_$clinit").ws().append("=").ws().append("function()").ws() + .append("{").softNewLine().indent(); + writer.appendClass(cls.getName()).append("_$clinit").ws().append("=").ws() + .append("function(){};").newLine(); + List stubNames = new ArrayList<>(); + for (MethodNode method : cls.getMethods()) { + renderBody(method); + stubNames.add(naming.getFullNameFor(method.getReference())); } - } - if (stubNames.size() > 0) { - writer.append("$rt_methodStubs(").appendClass(cls.getName()).append("_$clinit") - .append(",").ws().append("["); - for (int i = 0; i < stubNames.size(); ++i) { - if (i > 0) { - writer.append(",").ws(); + MethodHolder methodHolder = classSource.getClassHolder(cls.getName()).getMethod( + new MethodDescriptor("", ValueType.VOID)); + if (methodHolder != null) { + writer.appendMethodBody(new MethodReference(cls.getName(), methodHolder.getDescriptor())) + .append("();").softNewLine(); + } + writer.outdent().append("}").newLine(); + for (MethodNode method : cls.getMethods()) { + if (!method.getModifiers().contains(NodeModifier.STATIC)) { + renderDeclaration(method); } - writer.append("'").append(stubNames.get(i)).append("'"); } - writer.append("]);").newLine(); + if (stubNames.size() > 0) { + writer.append("$rt_methodStubs(").appendClass(cls.getName()).append("_$clinit") + .append(",").ws().append("["); + for (int i = 0; i < stubNames.size(); ++i) { + if (i > 0) { + writer.append(",").ws(); + } + writer.append("'").append(stubNames.get(i)).append("'"); + } + writer.append("]);").newLine(); + } } } catch (NamingException e) { throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e); @@ -489,7 +493,11 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(BreakStatement statement) { try { - writer.append("break ").append(statement.getTarget().getId()).append(";").softNewLine(); + writer.append("break"); + if (statement.getTarget() != null) { + writer.append(' ').append(statement.getTarget().getId()); + } + writer.append(";").softNewLine(); } catch (IOException e) { throw new RenderingException("IO error occured", e); } @@ -498,7 +506,11 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(ContinueStatement statement) { try { - writer.append("continue ").append(statement.getTarget().getId()).append(";").softNewLine(); + writer.append("continue"); + if (statement.getTarget() != null) { + writer.append(' ').append(statement.getTarget().getId()); + } + writer.append(";").softNewLine(); } catch (IOException e) { throw new RenderingException("IO error occured", e); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java index 16813b773..dcfcb3923 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java @@ -16,7 +16,9 @@ package org.teavm.javascript.ast; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; +import java.util.Set; /** * @@ -25,6 +27,7 @@ import java.util.List; public class ClassNode { private String name; private String parentName; + private Set modifiers = EnumSet.noneOf(NodeModifier.class); private List fields = new ArrayList<>(); private List methods = new ArrayList<>(); private List interfaces = new ArrayList<>(); @@ -53,4 +56,8 @@ public class ClassNode { public List getInterfaces() { return interfaces; } + + public Set getModifiers() { + return modifiers; + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java index f81781764..d91d0204c 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java @@ -20,5 +20,6 @@ package org.teavm.javascript.ast; * @author Alexey Andreev */ public enum NodeModifier { - STATIC + STATIC, + INTERFACE }