diff --git a/core/src/main/java/org/teavm/ast/BinaryOperation.java b/core/src/main/java/org/teavm/ast/BinaryOperation.java index 7c7023987..9384e7907 100644 --- a/core/src/main/java/org/teavm/ast/BinaryOperation.java +++ b/core/src/main/java/org/teavm/ast/BinaryOperation.java @@ -23,8 +23,6 @@ public enum BinaryOperation { MODULO, EQUALS, NOT_EQUALS, - STRICT_EQUALS, - STRICT_NOT_EQUALS, LESS, LESS_OR_EQUALS, GREATER, diff --git a/core/src/main/java/org/teavm/ast/BreakStatement.java b/core/src/main/java/org/teavm/ast/BreakStatement.java index 9b745251f..cfcb54ae7 100644 --- a/core/src/main/java/org/teavm/ast/BreakStatement.java +++ b/core/src/main/java/org/teavm/ast/BreakStatement.java @@ -15,10 +15,6 @@ */ package org.teavm.ast; -/** - * - * @author Alexey Andreev - */ public class BreakStatement extends Statement { private IdentifiedStatement target; private NodeLocation location; diff --git a/core/src/main/java/org/teavm/ast/IdentifiedStatement.java b/core/src/main/java/org/teavm/ast/IdentifiedStatement.java index 505323894..aef55069d 100644 --- a/core/src/main/java/org/teavm/ast/IdentifiedStatement.java +++ b/core/src/main/java/org/teavm/ast/IdentifiedStatement.java @@ -15,10 +15,6 @@ */ package org.teavm.ast; -/** - * - * @author Alexey Andreev - */ public abstract class IdentifiedStatement extends Statement { private String id; diff --git a/core/src/main/java/org/teavm/ast/InvocationExpr.java b/core/src/main/java/org/teavm/ast/InvocationExpr.java index 07fe3f237..00c139d1a 100644 --- a/core/src/main/java/org/teavm/ast/InvocationExpr.java +++ b/core/src/main/java/org/teavm/ast/InvocationExpr.java @@ -20,10 +20,6 @@ import java.util.List; import java.util.Map; import org.teavm.model.MethodReference; -/** - * - * @author Alexey Andreev - */ public class InvocationExpr extends Expr { private MethodReference method; private InvocationType type; diff --git a/core/src/main/java/org/teavm/ast/MethodNode.java b/core/src/main/java/org/teavm/ast/MethodNode.java index 020c671ce..3e15ffdb8 100644 --- a/core/src/main/java/org/teavm/ast/MethodNode.java +++ b/core/src/main/java/org/teavm/ast/MethodNode.java @@ -20,10 +20,6 @@ import java.util.List; import java.util.Set; import org.teavm.model.MethodReference; -/** - * - * @author Alexey Andreev - */ public abstract class MethodNode { private MethodReference reference; private Set modifiers = EnumSet.noneOf(NodeModifier.class); diff --git a/core/src/main/java/org/teavm/ast/RegularMethodNode.java b/core/src/main/java/org/teavm/ast/RegularMethodNode.java index e149f5c9a..19b827c25 100644 --- a/core/src/main/java/org/teavm/ast/RegularMethodNode.java +++ b/core/src/main/java/org/teavm/ast/RegularMethodNode.java @@ -20,10 +20,6 @@ import java.util.List; import java.util.Set; import org.teavm.model.MethodReference; -/** - * - * @author Alexey Andreev - */ public class RegularMethodNode extends MethodNode { private Statement body; private List variables = new ArrayList<>(); diff --git a/core/src/main/java/org/teavm/ast/WhileStatement.java b/core/src/main/java/org/teavm/ast/WhileStatement.java index fb99de7eb..bbdfd40fa 100644 --- a/core/src/main/java/org/teavm/ast/WhileStatement.java +++ b/core/src/main/java/org/teavm/ast/WhileStatement.java @@ -18,10 +18,6 @@ package org.teavm.ast; import java.util.ArrayList; import java.util.List; -/** - * - * @author Alexey Andreev - */ public class WhileStatement extends IdentifiedStatement { private Expr condition; private List body = new ArrayList<>(); diff --git a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java index 58bcbb8cb..c44781419 100644 --- a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java +++ b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java @@ -270,11 +270,11 @@ class StatementGenerator implements InstructionVisitor { insn.getConsequent(), insn.getAlternative()); break; case NOT_NULL: - branch(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, null, Expr.var(insn.getOperand().getIndex()), + branch(Expr.binary(BinaryOperation.NOT_EQUALS, null, Expr.var(insn.getOperand().getIndex()), Expr.constant(null)), insn.getConsequent(), insn.getAlternative()); break; case NULL: - branch(Expr.binary(BinaryOperation.STRICT_EQUALS, null, Expr.var(insn.getOperand().getIndex()), + branch(Expr.binary(BinaryOperation.EQUALS, null, Expr.var(insn.getOperand().getIndex()), Expr.constant(null)), insn.getConsequent(), insn.getAlternative()); break; } @@ -292,7 +292,7 @@ class StatementGenerator implements InstructionVisitor { consequent, alternative); break; case REFERENCE_EQUAL: - branch(withLocation(Expr.binary(BinaryOperation.STRICT_EQUALS, null, Expr.var(a), Expr.var(b))), + branch(withLocation(Expr.binary(BinaryOperation.EQUALS, null, Expr.var(a), Expr.var(b))), consequent, alternative); break; case NOT_EQUAL: @@ -300,7 +300,7 @@ class StatementGenerator implements InstructionVisitor { Expr.var(b))), consequent, alternative); break; case REFERENCE_NOT_EQUAL: - branch(withLocation(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, null, Expr.var(a), Expr.var(b))), + branch(withLocation(Expr.binary(BinaryOperation.NOT_EQUALS, null, Expr.var(a), Expr.var(b))), consequent, alternative); break; } diff --git a/core/src/main/java/org/teavm/ast/optimization/ExprOptimizer.java b/core/src/main/java/org/teavm/ast/optimization/ExprOptimizer.java index 34b174210..86241a697 100644 --- a/core/src/main/java/org/teavm/ast/optimization/ExprOptimizer.java +++ b/core/src/main/java/org/teavm/ast/optimization/ExprOptimizer.java @@ -48,10 +48,6 @@ final class ExprOptimizer { return Expr.binary(BinaryOperation.LESS_OR_EQUALS, binary.getType(), a, b, expr.getLocation()); case GREATER_OR_EQUALS: return Expr.binary(BinaryOperation.LESS, binary.getType(), a, b, expr.getLocation()); - case STRICT_EQUALS: - return Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, binary.getType(), a, b, expr.getLocation()); - case STRICT_NOT_EQUALS: - return Expr.binary(BinaryOperation.STRICT_EQUALS, binary.getType(), a, b, expr.getLocation()); default: break; } diff --git a/core/src/main/java/org/teavm/javascript/Renderer.java b/core/src/main/java/org/teavm/javascript/Renderer.java index 36c140599..b63a99411 100644 --- a/core/src/main/java/org/teavm/javascript/Renderer.java +++ b/core/src/main/java/org/teavm/javascript/Renderer.java @@ -1391,8 +1391,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext return Precedence.LOGICAL_AND; case OR: return Precedence.LOGICAL_OR; - case STRICT_EQUALS: - case STRICT_NOT_EQUALS: case EQUALS: case NOT_EQUALS: return Precedence.EQUALITY; @@ -1516,10 +1514,10 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext visitBinary(expr, "%", expr.getType() == OperationType.INT); break; case EQUALS: - visitBinary(expr, "==", false); + visitBinary(expr, "===", false); break; case NOT_EQUALS: - visitBinary(expr, "!=", false); + visitBinary(expr, "!==", false); break; case GREATER: visitBinary(expr, ">", false); @@ -1533,12 +1531,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext case LESS_OR_EQUALS: visitBinary(expr, "<=", false); break; - case STRICT_EQUALS: - visitBinary(expr, "===", false); - break; - case STRICT_NOT_EQUALS: - visitBinary(expr, "!==", false); - break; case COMPARE: visitBinaryFunction(expr, naming.getNameForFunction("$rt_compare")); break; diff --git a/core/src/main/java/org/teavm/wasm/Example.java b/core/src/main/java/org/teavm/wasm/Example.java new file mode 100644 index 000000000..92160d69c --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/Example.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 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.wasm; + +public final class Example { + private Example() { + } + + public static void main(String[] args) { + int a = 0; + int b = 1; + for (int i = 0; i < 10; ++i) { + int c = a + b; + a = b; + b = c; + } + } +} diff --git a/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java new file mode 100644 index 000000000..884ccac28 --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java @@ -0,0 +1,620 @@ +/* + * Copyright 2016 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.wasm.generate; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.teavm.ast.AssignmentStatement; +import org.teavm.ast.BinaryExpr; +import org.teavm.ast.BlockStatement; +import org.teavm.ast.BreakStatement; +import org.teavm.ast.CastExpr; +import org.teavm.ast.ConditionalExpr; +import org.teavm.ast.ConditionalStatement; +import org.teavm.ast.ConstantExpr; +import org.teavm.ast.ContinueStatement; +import org.teavm.ast.Expr; +import org.teavm.ast.ExprVisitor; +import org.teavm.ast.GotoPartStatement; +import org.teavm.ast.IdentifiedStatement; +import org.teavm.ast.InitClassStatement; +import org.teavm.ast.InstanceOfExpr; +import org.teavm.ast.InvocationExpr; +import org.teavm.ast.InvocationType; +import org.teavm.ast.MonitorEnterStatement; +import org.teavm.ast.MonitorExitStatement; +import org.teavm.ast.NewArrayExpr; +import org.teavm.ast.NewExpr; +import org.teavm.ast.NewMultiArrayExpr; +import org.teavm.ast.OperationType; +import org.teavm.ast.PrimitiveCastExpr; +import org.teavm.ast.QualificationExpr; +import org.teavm.ast.ReturnStatement; +import org.teavm.ast.SequentialStatement; +import org.teavm.ast.Statement; +import org.teavm.ast.StatementVisitor; +import org.teavm.ast.SubscriptExpr; +import org.teavm.ast.SwitchClause; +import org.teavm.ast.SwitchStatement; +import org.teavm.ast.ThrowStatement; +import org.teavm.ast.TryCatchStatement; +import org.teavm.ast.UnaryExpr; +import org.teavm.ast.UnwrapArrayExpr; +import org.teavm.ast.VariableExpr; +import org.teavm.ast.WhileStatement; +import org.teavm.wasm.model.WasmFunction; +import org.teavm.wasm.model.WasmLocal; +import org.teavm.wasm.model.expression.WasmAssignment; +import org.teavm.wasm.model.expression.WasmBlock; +import org.teavm.wasm.model.expression.WasmBranch; +import org.teavm.wasm.model.expression.WasmBreak; +import org.teavm.wasm.model.expression.WasmCall; +import org.teavm.wasm.model.expression.WasmConditional; +import org.teavm.wasm.model.expression.WasmConversion; +import org.teavm.wasm.model.expression.WasmExpression; +import org.teavm.wasm.model.expression.WasmFloat32Constant; +import org.teavm.wasm.model.expression.WasmFloat64Constant; +import org.teavm.wasm.model.expression.WasmFloatBinary; +import org.teavm.wasm.model.expression.WasmFloatBinaryOperation; +import org.teavm.wasm.model.expression.WasmFloatType; +import org.teavm.wasm.model.expression.WasmInt32Constant; +import org.teavm.wasm.model.expression.WasmInt64Constant; +import org.teavm.wasm.model.expression.WasmIntBinary; +import org.teavm.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.wasm.model.expression.WasmIntType; +import org.teavm.wasm.model.expression.WasmLocalReference; +import org.teavm.wasm.model.expression.WasmReturn; +import org.teavm.wasm.model.expression.WasmSwitch; + +class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { + private WasmFunction function; + private IdentifiedStatement currentContinueTarget; + private IdentifiedStatement currentBreakTarget; + private Map breakTargets = new HashMap<>(); + private Map continueTargets = new HashMap<>(); + private Set usedBlocks = new HashSet<>(); + WasmExpression result; + + public WasmGenerationVisitor(WasmFunction function) { + this.function = function; + } + + @Override + public void visit(BinaryExpr expr) { + switch (expr.getOperation()) { + case ADD: + generateBinary(WasmIntBinaryOperation.ADD, WasmFloatBinaryOperation.ADD, expr); + break; + case SUBTRACT: + generateBinary(WasmIntBinaryOperation.SUB, WasmFloatBinaryOperation.ADD, expr); + break; + case MULTIPLY: + generateBinary(WasmIntBinaryOperation.MUL, WasmFloatBinaryOperation.ADD, expr); + break; + case DIVIDE: + generateBinary(WasmIntBinaryOperation.DIV_SIGNED, WasmFloatBinaryOperation.DIV, expr); + break; + case MODULO: { + switch (expr.getType()) { + case INT: + case LONG: + generateBinary(WasmIntBinaryOperation.REM_SIGNED, expr); + break; + default: + WasmCall call = new WasmCall("rt$remainder." + typeAsString(expr.getType()), false); + expr.getFirstOperand().acceptVisitor(this); + call.getArguments().add(result); + expr.getSecondOperand().acceptVisitor(this); + call.getArguments().add(result); + result = call; + break; + } + + break; + } + case BITWISE_AND: + generateBinary(WasmIntBinaryOperation.AND, expr); + break; + case BITWISE_OR: + generateBinary(WasmIntBinaryOperation.OR, expr); + break; + case BITWISE_XOR: + generateBinary(WasmIntBinaryOperation.XOR, expr); + break; + case EQUALS: + generateBinary(WasmIntBinaryOperation.EQ, WasmFloatBinaryOperation.EQ, expr); + break; + case NOT_EQUALS: + generateBinary(WasmIntBinaryOperation.NE, WasmFloatBinaryOperation.NE, expr); + break; + case GREATER: + generateBinary(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT, expr); + break; + case GREATER_OR_EQUALS: + generateBinary(WasmIntBinaryOperation.GE_SIGNED, WasmFloatBinaryOperation.GE, expr); + break; + case LESS: + generateBinary(WasmIntBinaryOperation.LT_SIGNED, WasmFloatBinaryOperation.LT, expr); + break; + case LESS_OR_EQUALS: + generateBinary(WasmIntBinaryOperation.LE_SIGNED, WasmFloatBinaryOperation.LE, expr); + break; + case LEFT_SHIFT: + generateBinary(WasmIntBinaryOperation.SHL, expr); + break; + case RIGHT_SHIFT: + generateBinary(WasmIntBinaryOperation.SHR_SIGNED, expr); + break; + case UNSIGNED_RIGHT_SHIFT: + generateBinary(WasmIntBinaryOperation.SHR_UNSIGNED, expr); + break; + case COMPARE: { + WasmCall call = new WasmCall("rt$compare." + typeAsString(expr.getType()), false); + expr.getFirstOperand().acceptVisitor(this); + call.getArguments().add(result); + expr.getSecondOperand().acceptVisitor(this); + call.getArguments().add(result); + result = call; + break; + } + case AND: + generateAnd(expr); + break; + case OR: + generateOr(expr); + break; + } + } + + private void generateBinary(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp, BinaryExpr expr) { + expr.getFirstOperand().acceptVisitor(this); + WasmExpression first = result; + expr.getSecondOperand().acceptVisitor(this); + WasmExpression second = result; + + if (expr.getType() == null) { + result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + } else { + switch (expr.getType()) { + case INT: + result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + break; + case LONG: + result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second); + break; + case FLOAT: + result = new WasmFloatBinary(WasmFloatType.FLOAT32, floatOp, first, second); + break; + case DOUBLE: + result = new WasmFloatBinary(WasmFloatType.FLOAT64, floatOp, first, second); + break; + } + } + } + + private void generateBinary(WasmIntBinaryOperation intOp, BinaryExpr expr) { + expr.getFirstOperand().acceptVisitor(this); + WasmExpression first = result; + expr.getSecondOperand().acceptVisitor(this); + WasmExpression second = result; + + switch (expr.getType()) { + case INT: + result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + break; + case LONG: + result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second); + break; + case FLOAT: + case DOUBLE: + throw new AssertionError("Can't translate operation " + intOp + " for type " + expr.getType()); + } + } + + private String typeAsString(OperationType type) { + switch (type) { + case INT: + return "i32"; + case LONG: + return "i64"; + case FLOAT: + return "float"; + case DOUBLE: + return "double"; + } + throw new AssertionError(type.toString()); + } + + private void generateAnd(BinaryExpr expr) { + WasmBlock block = new WasmBlock(false); + + expr.getFirstOperand().acceptVisitor(this); + WasmBranch branch = new WasmBranch(negate(result), block); + branch.setResult(new WasmInt32Constant(0)); + block.getBody().add(branch); + + expr.getSecondOperand().acceptVisitor(this); + block.getBody().add(result); + + result = block; + } + + private void generateOr(BinaryExpr expr) { + WasmBlock block = new WasmBlock(false); + + expr.getFirstOperand().acceptVisitor(this); + WasmBranch branch = new WasmBranch(result, block); + branch.setResult(new WasmInt32Constant(1)); + block.getBody().add(branch); + + expr.getSecondOperand().acceptVisitor(this); + block.getBody().add(result); + + result = block; + } + + @Override + public void visit(UnaryExpr expr) { + switch (expr.getOperation()) { + case INT_TO_BYTE: + expr.getOperand().acceptVisitor(this); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + result, new WasmInt32Constant(24)); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, + result, new WasmInt32Constant(24)); + break; + case INT_TO_SHORT: + expr.getOperand().acceptVisitor(this); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + result, new WasmInt32Constant(16)); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, + result, new WasmInt32Constant(16)); + break; + case INT_TO_CHAR: + expr.getOperand().acceptVisitor(this); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + result, new WasmInt32Constant(16)); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED, + result, new WasmInt32Constant(16)); + break; + case LENGTH: + result = new WasmInt32Constant(0); + break; + case NOT: + expr.getOperand().acceptVisitor(this); + result = negate(result); + break; + case NEGATE: + expr.getOperand().acceptVisitor(this); + switch (expr.getType()) { + case INT: + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, + new WasmInt32Constant(0), result); + break; + case LONG: + result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.SUB, + new WasmInt64Constant(0), result); + break; + case FLOAT: + result = new WasmFloatBinary(WasmFloatType.FLOAT32, WasmFloatBinaryOperation.SUB, + new WasmFloat32Constant(0), result); + break; + case DOUBLE: + result = new WasmFloatBinary(WasmFloatType.FLOAT64, WasmFloatBinaryOperation.SUB, + new WasmFloat64Constant(0), result); + break; + } + break; + case NULL_CHECK: + expr.getOperand().acceptVisitor(this); + break; + } + } + + @Override + public void visit(AssignmentStatement statement) { + Expr left = statement.getLeftValue(); + if (left instanceof VariableExpr) { + VariableExpr varExpr = (VariableExpr) left; + WasmLocal local = function.getLocalVariables().get(varExpr.getIndex()); + statement.getRightValue().acceptVisitor(this); + result = new WasmAssignment(local, result); + } else { + throw new UnsupportedOperationException("This expression is not supported yet"); + } + } + + @Override + public void visit(ConditionalExpr expr) { + expr.getCondition().acceptVisitor(this); + WasmConditional conditional = new WasmConditional(result); + expr.getConsequent().acceptVisitor(this); + conditional.getThenBlock().getBody().add(result); + expr.getAlternative().acceptVisitor(this); + conditional.getThenBlock().getBody().add(result); + result = conditional; + } + + @Override + public void visit(SequentialStatement statement) { + WasmBlock block = new WasmBlock(false); + for (Statement part : statement.getSequence()) { + part.acceptVisitor(this); + block.getBody().add(result); + } + result = block; + } + + @Override + public void visit(ConstantExpr expr) { + if (expr.getValue() == null) { + result = new WasmInt32Constant(0); + } else if (expr.getValue() instanceof Integer) { + result = new WasmInt32Constant((Integer) expr.getValue()); + } else if (expr.getValue() instanceof Long) { + result = new WasmInt64Constant((Long) expr.getValue()); + } else if (expr.getValue() instanceof Float) { + result = new WasmFloat32Constant((Float) expr.getValue()); + } else if (expr.getValue() instanceof Double) { + result = new WasmFloat64Constant((Double) expr.getValue()); + } else { + throw new IllegalArgumentException("Constant unsupported: " + expr.getValue()); + } + } + + @Override + public void visit(ConditionalStatement statement) { + statement.getCondition().acceptVisitor(this); + WasmConditional conditional = new WasmConditional(result); + for (Statement part : statement.getConsequent()) { + part.acceptVisitor(this); + conditional.getThenBlock().getBody().add(result); + } + for (Statement part : statement.getAlternative()) { + part.acceptVisitor(this); + conditional.getElseBlock().getBody().add(result); + } + result = conditional; + } + + @Override + public void visit(VariableExpr expr) { + result = new WasmLocalReference(function.getLocalVariables().get(expr.getIndex())); + } + + @Override + public void visit(SubscriptExpr expr) { + throw new UnsupportedOperationException("Not supported yet"); + } + + @Override + public void visit(SwitchStatement statement) { + List wrappers = new ArrayList<>(); + + WasmBlock wrapper = new WasmBlock(false); + statement.getValue().acceptVisitor(this); + WasmSwitch wasmSwitch = new WasmSwitch(result, wrapper); + wrapper.getBody().add(wasmSwitch); + + WasmBlock defaultBlock = new WasmBlock(false); + defaultBlock.getBody().add(wrapper); + for (Statement part : statement.getDefaultClause()) { + part.acceptVisitor(this); + defaultBlock.getBody().add(result); + } + wrapper = defaultBlock; + + for (SwitchClause clause : statement.getClauses()) { + WasmBlock caseBlock = new WasmBlock(false); + caseBlock.getBody().add(wrapper); + wasmSwitch.getTargets().add(wrapper); + for (Statement part : clause.getBody()) { + part.acceptVisitor(this); + caseBlock.getBody().add(result); + } + wrappers.add(caseBlock); + wrapper = caseBlock; + } + + for (WasmBlock nestedWrapper : wrappers) { + nestedWrapper.getBody().add(new WasmBreak(wrapper)); + } + + result = wrapper; + } + + @Override + public void visit(UnwrapArrayExpr expr) { + expr.getArray().acceptVisitor(this); + } + + @Override + public void visit(WhileStatement statement) { + WasmBlock wrapper = new WasmBlock(false); + WasmBlock loop = new WasmBlock(true); + + continueTargets.put(statement, loop); + breakTargets.put(statement, wrapper); + IdentifiedStatement oldBreakTarget = currentBreakTarget; + IdentifiedStatement oldContinueTarget = currentContinueTarget; + currentBreakTarget = statement; + currentContinueTarget = statement; + + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + loop.getBody().add(result); + } + + currentBreakTarget = oldBreakTarget; + currentContinueTarget = oldContinueTarget; + continueTargets.remove(statement); + breakTargets.remove(statement); + + if (usedBlocks.contains(wrapper)) { + wrapper.getBody().add(loop); + result = wrapper; + } else { + result = loop; + } + } + + @Override + public void visit(InvocationExpr expr) { + if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { + String methodName = WasmMangling.mangleMethod(expr.getMethod()); + WasmCall call = new WasmCall(methodName); + for (Expr argument : expr.getArguments()) { + argument.acceptVisitor(this); + call.getArguments().add(result); + } + result = call; + } + } + + @Override + public void visit(BlockStatement statement) { + WasmBlock block = new WasmBlock(false); + + if (statement.getId() != null) { + breakTargets.put(statement, block); + } + + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + block.getBody().add(result); + } + + if (statement.getId() != null) { + breakTargets.remove(statement); + } + + result = block; + } + + @Override + public void visit(QualificationExpr expr) { + } + + @Override + public void visit(BreakStatement statement) { + IdentifiedStatement target = statement.getTarget(); + if (target == null) { + target = currentBreakTarget; + } + WasmBlock wasmTarget = breakTargets.get(target); + usedBlocks.add(wasmTarget); + result = new WasmBreak(wasmTarget); + } + + @Override + public void visit(NewExpr expr) { + } + + @Override + public void visit(ContinueStatement statement) { + IdentifiedStatement target = statement.getTarget(); + if (target == null) { + target = currentContinueTarget; + } + WasmBlock wasmTarget = continueTargets.get(target); + usedBlocks.add(wasmTarget); + result = new WasmBreak(wasmTarget); + } + + @Override + public void visit(NewArrayExpr expr) { + } + + @Override + public void visit(NewMultiArrayExpr expr) { + } + + @Override + public void visit(ReturnStatement statement) { + if (statement.getResult() != null) { + statement.getResult().acceptVisitor(this); + } else { + result = null; + } + result = new WasmReturn(result); + } + + @Override + public void visit(InstanceOfExpr expr) { + + } + + @Override + public void visit(ThrowStatement statement) { + + } + + @Override + public void visit(CastExpr expr) { + expr.getValue().acceptVisitor(this); + } + + @Override + public void visit(InitClassStatement statement) { + } + + @Override + public void visit(PrimitiveCastExpr expr) { + expr.getValue().acceptVisitor(this); + result = new WasmConversion(WasmGeneratorUtil.mapType(expr.getSource()), + WasmGeneratorUtil.mapType(expr.getTarget()), true, result); + + } + + @Override + public void visit(TryCatchStatement statement) { + } + + @Override + public void visit(GotoPartStatement statement) { + } + + @Override + public void visit(MonitorEnterStatement statement) { + } + + @Override + public void visit(MonitorExitStatement statement) { + } + + private WasmExpression negate(WasmExpression expr) { + if (expr instanceof WasmIntBinary) { + WasmIntBinary binary = (WasmIntBinary) expr; + if (binary.getType() == WasmIntType.INT32 && binary.getOperation() == WasmIntBinaryOperation.XOR) { + if (isOne(binary.getFirst())) { + return binary.getSecond(); + } + if (isOne(binary.getSecond())) { + return binary.getFirst(); + } + } + } + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.XOR, expr, new WasmInt32Constant(1)); + } + + private boolean isOne(WasmExpression expression) { + return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 1; + } +} diff --git a/core/src/main/java/org/teavm/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/wasm/generate/WasmGenerator.java index 3c3321f70..cce5e1073 100644 --- a/core/src/main/java/org/teavm/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/wasm/generate/WasmGenerator.java @@ -15,5 +15,55 @@ */ package org.teavm.wasm.generate; +import org.teavm.ast.RegularMethodNode; +import org.teavm.ast.decompilation.Decompiler; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.ValueType; +import org.teavm.model.util.TypeInferer; +import org.teavm.model.util.VariableType; +import org.teavm.wasm.model.WasmFunction; +import org.teavm.wasm.model.WasmLocal; + public class WasmGenerator { + private Decompiler decompiler; + private ClassHolderSource classSource; + + public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource) { + this.decompiler = decompiler; + this.classSource = classSource; + } + + public WasmFunction generate(MethodReference methodReference) { + ClassHolder cls = classSource.get(methodReference.getClassName()); + MethodHolder method = cls.getMethod(methodReference.getDescriptor()); + Program program = method.getProgram(); + + RegularMethodNode methodAst = decompiler.decompileRegular(method); + TypeInferer inferer = new TypeInferer(); + inferer.inferTypes(program, methodReference); + + WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference)); + for (int i = 0; i < methodAst.getVariables().size(); ++i) { + int varIndex = methodAst.getVariables().get(i); + VariableType type = inferer.typeOf(varIndex); + function.add(new WasmLocal(WasmGeneratorUtil.mapType(type))); + } + + for (int i = 0; i <= methodReference.parameterCount(); ++i) { + function.getParameters().add(function.getLocalVariables().get(i).getType()); + } + if (methodReference.getReturnType() != ValueType.VOID) { + function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType())); + } + + WasmGenerationVisitor visitor = new WasmGenerationVisitor(function); + methodAst.getBody().acceptVisitor(visitor); + function.getBody().add(visitor.result); + + return function; + } } diff --git a/core/src/main/java/org/teavm/wasm/generate/WasmGeneratorUtil.java b/core/src/main/java/org/teavm/wasm/generate/WasmGeneratorUtil.java new file mode 100644 index 000000000..b3657fbc6 --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/generate/WasmGeneratorUtil.java @@ -0,0 +1,75 @@ +/* + * Copyright 2016 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.wasm.generate; + +import org.teavm.ast.OperationType; +import org.teavm.model.ValueType; +import org.teavm.model.util.VariableType; +import org.teavm.wasm.model.WasmType; + +public final class WasmGeneratorUtil { + private WasmGeneratorUtil() { + } + + public static WasmType mapType(OperationType type) { + switch (type) { + case INT: + return WasmType.INT32; + case LONG: + return WasmType.INT64; + case FLOAT: + return WasmType.FLOAT32; + case DOUBLE: + return WasmType.FLOAT64; + } + throw new IllegalArgumentException(type.toString()); + } + + public static WasmType mapType(ValueType type) { + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + case BYTE: + case SHORT: + case CHARACTER: + case INTEGER: + return WasmType.INT32; + case LONG: + return WasmType.INT64; + case FLOAT: + return WasmType.FLOAT32; + case DOUBLE: + return WasmType.FLOAT64; + } + } + return WasmType.INT32; + } + + public static WasmType mapType(VariableType type) { + switch (type) { + case INT: + return WasmType.INT32; + case LONG: + return WasmType.INT64; + case FLOAT: + return WasmType.FLOAT32; + case DOUBLE: + return WasmType.FLOAT64; + default: + return WasmType.INT32; + } + } +} diff --git a/core/src/main/java/org/teavm/wasm/generate/WasmMangling.java b/core/src/main/java/org/teavm/wasm/generate/WasmMangling.java new file mode 100644 index 000000000..bf65e172e --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/generate/WasmMangling.java @@ -0,0 +1,97 @@ +/* + * Copyright 2016 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.wasm.generate; + +import java.util.Arrays; +import java.util.stream.Collectors; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +public final class WasmMangling { + private WasmMangling() { + } + + public static String mangleMethod(MethodReference method) { + StringBuilder sb = new StringBuilder("method$" + mangleString(method.getClassName()) + "$"); + String name = mangleString(method.getName()); + sb.append(mangleType(method.getReturnType())); + sb.append(name.length() + "_" + name); + sb.append(Arrays.stream(method.getParameterTypes()) + .map(WasmMangling::mangleType) + .collect(Collectors.joining())); + return sb.toString(); + } + + private static String mangleString(String string) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < string.length(); ++i) { + char c = string.charAt(i); + switch (c) { + case '$': + case '.': + case '-': + sb.append(c); + break; + case '_': + sb.append("__"); + break; + default: + if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9') { + sb.append(c); + } else { + sb.append('_') + .append(Character.forDigit(c >>> 12, 16)) + .append(Character.forDigit((c >>> 8) & 0xF, 16)) + .append(Character.forDigit((c >>> 4) & 0xF, 16)) + .append(Character.forDigit(c & 0xF, 16)); + } + break; + } + } + return sb.toString(); + } + + public static String mangleType(ValueType type) { + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + return "Z"; + case BYTE: + return "B"; + case SHORT: + return "S"; + case CHARACTER: + return "C"; + case INTEGER: + return "I"; + case LONG: + return "L"; + case FLOAT: + return "F"; + case DOUBLE: + return "D"; + } + } else if (type instanceof ValueType.Void) { + return "V"; + } else if (type instanceof ValueType.Array) { + return "A" + mangleType(((ValueType.Array) type).getItemType()); + } else if (type instanceof ValueType.Object) { + String className = ((ValueType.Object) type).getClassName(); + return "L" + className.length() + "_" + className; + } + throw new IllegalArgumentException("Don't know how to mangle " + type); + } +} diff --git a/core/src/main/java/org/teavm/wasm/model/expression/WasmBranch.java b/core/src/main/java/org/teavm/wasm/model/expression/WasmBranch.java index 598262477..cf8240e01 100644 --- a/core/src/main/java/org/teavm/wasm/model/expression/WasmBranch.java +++ b/core/src/main/java/org/teavm/wasm/model/expression/WasmBranch.java @@ -20,8 +20,11 @@ import java.util.Objects; public class WasmBranch extends WasmExpression { private WasmExpression condition; private WasmBlock target; + private WasmExpression result; public WasmBranch(WasmExpression condition, WasmBlock target) { + Objects.requireNonNull(condition); + Objects.requireNonNull(target); this.condition = condition; this.target = target; } @@ -43,4 +46,12 @@ public class WasmBranch extends WasmExpression { Objects.requireNonNull(target); this.target = target; } + + public WasmExpression getResult() { + return result; + } + + public void setResult(WasmExpression result) { + this.result = result; + } } diff --git a/core/src/main/java/org/teavm/wasm/model/expression/WasmBreak.java b/core/src/main/java/org/teavm/wasm/model/expression/WasmBreak.java new file mode 100644 index 000000000..e52993f92 --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/model/expression/WasmBreak.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 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.wasm.model.expression; + +import java.util.Objects; + +public class WasmBreak extends WasmExpression { + private WasmBlock target; + private WasmExpression result; + + public WasmBreak(WasmBlock target) { + Objects.requireNonNull(target); + this.target = target; + } + + public WasmBlock getTarget() { + return target; + } + + public void setTarget(WasmBlock target) { + Objects.requireNonNull(target); + this.target = target; + } + + public WasmExpression getResult() { + return result; + } + + public void setResult(WasmExpression result) { + this.result = result; + } +} diff --git a/core/src/main/java/org/teavm/wasm/runtime/WasmRuntime.java b/core/src/main/java/org/teavm/wasm/runtime/WasmRuntime.java new file mode 100644 index 000000000..ece9fdee6 --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/runtime/WasmRuntime.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 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.wasm.runtime; + +public final class WasmRuntime { + private WasmRuntime() { + } + + public static int compare(int a, int b) { + return a > b ? 1 : a < b ? -1 : 0; + } + + public static int compare(long a, long b) { + return a > b ? 1 : a < b ? -1 : 0; + } + + public static int compare(float a, float b) { + return a > b ? 1 : a < b ? -1 : 0; + } + + public static int compare(double a, double b) { + return a > b ? 1 : a < b ? -1 : 0; + } + + public static float remainder(float a, float b) { + return a - (float) (int) (a / b) * b; + } + + public static double remainder(double a, double b) { + return a - (double) (long) (a / b) * b; + } +}