/* * This file is a part of Alchemy OS project. * Copyright (C) 2011-2014, Sergey Basalaev <sbasalaev@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package alchemy.nec; import alchemy.evm.EtherLoader; import alchemy.evm.Opcodes; import alchemy.nec.asm.FunctionWriter; import alchemy.nec.asm.Label; import alchemy.nec.asm.UnitWriter; import alchemy.nec.syntax.Function; import alchemy.nec.syntax.Null; import alchemy.nec.syntax.Unit; import alchemy.nec.syntax.Var; import alchemy.nec.syntax.expr.*; import alchemy.nec.syntax.statement.*; import alchemy.nec.syntax.type.ArrayType; import alchemy.nec.syntax.type.Type; import alchemy.types.Int32; import alchemy.types.Int64; import alchemy.util.ArrayList; import alchemy.util.Arrays; import java.io.IOException; import java.io.OutputStream; /** * Writes bytecode of the parsed unit. * @author Sergey Basalaev */ public final class EAsmWriter implements ExprVisitor, StatementVisitor { private final CompilerEnv env; private final FlowAnalyzer flow; private Unit unit; private FunctionWriter writer; private Label loopStart; private Label loopEnd; private Var[] localVars = new Var[256]; public EAsmWriter(CompilerEnv env) { env.suppressWarnings(); this.env = env; this.flow = new FlowAnalyzer(env); } private int getVarIndex(Var var) { for (int i=0; i<256; i++) { if (localVars[i] == var) return i; } return -1; } private int addVar(Var var) { for (int i=0; i<256; i++) { if (localVars[i] == null) { localVars[i] = var; return i; } } throw new RuntimeException("Too many variables"); } private void removeVar(Var var) { for (int i=0; i<256; i++) { if (localVars[i] == var) { localVars[i] = null; } } } private boolean flowContinues(Statement stat) { return stat.accept(flow, (loopStart != null) ? Boolean.TRUE : Boolean.FALSE) == flow.NEXT; } public void writeTo(Unit unit, OutputStream out) throws IOException { this.unit = unit; UnitWriter uw = new UnitWriter(); uw.visitVersion(EtherLoader.VERSION); ArrayList funcs = unit.implementedFunctions; for (int i=0; i<funcs.size(); i++) { Function f = (Function) funcs.get(i); try { writer = uw.visitFunction(f.signature, f.isPublic, f.type.argtypes.length); if (env.debug) writer.visitSource(f.source); for (int vi=0; vi<f.args.length; vi++) addVar(f.args[vi]); f.body.accept(this, null); for (int vi=0; vi<f.args.length; vi++) removeVar(f.args[vi]); writer.visitEnd(); } catch (Exception e) { env.exceptionHappened("Assembler", "Format: EAsm 2.2\nFunction: " + f.signature, e); } } uw.writeTo(out); } /** * Visits condition of conditional expressions. * Argument <i>cond</i> specifies value of <i>expr</i> * on which jump should be performed. */ private void visitCondition(Expr expr, Label jumpto, boolean cond) { switch (expr.kind) { case Expr.EXPR_CONST: { // true or false, jump if matches cond Object value = ((ConstExpr)expr).value; if (value != Boolean.TRUE ^ cond) { writer.visitJumpInsn(Opcodes.GOTO, jumpto); } break; } case Expr.EXPR_UNARY: { // can be only !expr UnaryExpr unary = (UnaryExpr) expr; visitCondition(unary.expr, jumpto, !cond); break; } case Expr.EXPR_IF: { IfElseExpr ifelse = (IfElseExpr) expr; Label elsebranch = new Label(); Label afterif = new Label(); if (ifelse.ifexpr.kind == Expr.EXPR_CONST) { Object cnst = ((ConstExpr)ifelse.ifexpr).value; if (cnst == Boolean.TRUE ^ cond) { visitCondition(ifelse.condition, afterif, true); visitCondition(ifelse.elseexpr, jumpto, cond); writer.visitLabel(afterif); } else { visitCondition(ifelse.condition, jumpto, true); visitCondition(ifelse.elseexpr, jumpto, cond); writer.visitLabel(afterif); } } else if (ifelse.elseexpr.kind == Expr.EXPR_CONST) { Object cnst = ((ConstExpr)ifelse.elseexpr).value; if (cnst == Boolean.TRUE ^ cond) { visitCondition(ifelse.condition, afterif, false); visitCondition(ifelse.ifexpr, jumpto, cond); writer.visitLabel(afterif); } else { visitCondition(ifelse.condition, jumpto, false); visitCondition(ifelse.ifexpr, jumpto, cond); writer.visitLabel(afterif); } } else { visitCondition(ifelse.condition, elsebranch, false); visitCondition(ifelse.ifexpr, jumpto, cond); writer.visitJumpInsn(Opcodes.GOTO, afterif); writer.visitLabel(elsebranch); visitCondition(ifelse.elseexpr, jumpto, cond); writer.visitLabel(afterif); } break; } case Expr.EXPR_COMPARISON: { ComparisonExpr cmp = (ComparisonExpr) expr; if (cmp.rhs.kind == Expr.EXPR_CONST && ((ConstExpr)cmp.rhs).value == Null.NULL) { // comparison with null cmp.lhs.accept(this, null); if (cmp.operator == Token.EQEQ) { writer.visitJumpInsn(cond ? Opcodes.IFNULL : Opcodes.IFNNULL, jumpto); } else { writer.visitJumpInsn(cond ? Opcodes.IFNNULL : Opcodes.IFNULL, jumpto); } } else if (cmp.rhs.kind == Expr.EXPR_CONST && ((ConstExpr)cmp.rhs).value.equals(Int32.ZERO) && cmp.lhs.returnType().kind == Type.TYPE_INT) { // integer comparison with zero cmp.lhs.accept(this, null); switch (cmp.operator) { case '<': writer.visitJumpInsn(cond ? Opcodes.IFLT : Opcodes.IFGE, jumpto); break; case '>': writer.visitJumpInsn(cond ? Opcodes.IFGT : Opcodes.IFLE, jumpto); break; case Token.LTEQ: writer.visitJumpInsn(cond ? Opcodes.IFLE : Opcodes.IFGT, jumpto); break; case Token.GTEQ: writer.visitJumpInsn(cond ? Opcodes.IFGE : Opcodes.IFLT, jumpto); break; case Token.EQEQ: writer.visitJumpInsn(cond ? Opcodes.IFEQ : Opcodes.IFNE, jumpto); break; case Token.NOTEQ: writer.visitJumpInsn(cond ? Opcodes.IFNE : Opcodes.IFEQ, jumpto); break; } } else if ((cmp.lhs.returnType().kind == Type.TYPE_INT || cmp.rhs.returnType().kind == Type.TYPE_INT) && cmp.operator != Token.EQEQ && cmp.operator != Token.NOTEQ) { // integer comparison cmp.lhs.accept(this, null); cmp.rhs.accept(this, null); switch (cmp.operator) { case '<': writer.visitJumpInsn(cond ? Opcodes.IF_ICMPLT : Opcodes.IF_ICMPGE, jumpto); break; case '>': writer.visitJumpInsn(cond ? Opcodes.IF_ICMPGT : Opcodes.IF_ICMPLE, jumpto); break; case Token.LTEQ: writer.visitJumpInsn(cond ? Opcodes.IF_ICMPLE : Opcodes.IF_ICMPGT, jumpto); break; case Token.GTEQ: writer.visitJumpInsn(cond ? Opcodes.IF_ICMPGE : Opcodes.IF_ICMPLT, jumpto); break; } } else if (cmp.operator == Token.EQEQ || cmp.operator == Token.NOTEQ) { // object comparison cmp.lhs.accept(this, null); cmp.rhs.accept(this, null); if (cmp.operator == Token.EQEQ) { writer.visitJumpInsn(cond ? Opcodes.IF_ACMPEQ : Opcodes.IF_ACMPNE, jumpto); } else { writer.visitJumpInsn(cond ? Opcodes.IF_ACMPNE : Opcodes.IF_ACMPEQ, jumpto); } } else { // general comparison cmp.lhs.accept(this, null); cmp.rhs.accept(this, null); Type type = Type.commonSuperType(cmp.lhs.returnType(), cmp.rhs.returnType()); switch (type.kind) { case Type.TYPE_INT: writer.visitInsn(Opcodes.ICMP); break; case Type.TYPE_LONG: writer.visitInsn(Opcodes.LCMP); break; case Type.TYPE_FLOAT: writer.visitInsn(Opcodes.FCMP); break; case Type.TYPE_DOUBLE: writer.visitInsn(Opcodes.DCMP); break; default: writer.visitInsn(Opcodes.ACMP); break; } switch (cmp.operator) { case '<': writer.visitJumpInsn(cond ? Opcodes.IFLT : Opcodes.IFGE, jumpto); break; case '>': writer.visitJumpInsn(cond ? Opcodes.IFGE : Opcodes.IFLE, jumpto); break; case Token.LTEQ: writer.visitJumpInsn(cond ? Opcodes.IFLE : Opcodes.IFGT, jumpto); break; case Token.GTEQ: writer.visitJumpInsn(cond ? Opcodes.IFGE : Opcodes.IFLT, jumpto); break; case Token.EQEQ: writer.visitJumpInsn(cond ? Opcodes.IFEQ : Opcodes.IFNE, jumpto); break; case Token.NOTEQ: writer.visitJumpInsn(cond ? Opcodes.IFNE : Opcodes.IFEQ, jumpto); break; } } break; } default: expr.accept(this, null); writer.visitJumpInsn(cond ? Opcodes.IFNE : Opcodes.IFEQ, jumpto); } } private int binaryOperatorInsn(Type type, int operator) { int kind = type.kind; switch (operator) { case '+': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.IADD; case Type.TYPE_LONG: return Opcodes.LADD; case Type.TYPE_FLOAT: return Opcodes.FADD; case Type.TYPE_DOUBLE: return Opcodes.DADD; } break; case '-': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.ISUB; case Type.TYPE_LONG: return Opcodes.LSUB; case Type.TYPE_FLOAT: return Opcodes.FSUB; case Type.TYPE_DOUBLE: return Opcodes.DSUB; } break; case '*': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.IMUL; case Type.TYPE_LONG: return Opcodes.LMUL; case Type.TYPE_FLOAT: return Opcodes.FMUL; case Type.TYPE_DOUBLE: return Opcodes.DMUL; } break; case '/': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.IDIV; case Type.TYPE_LONG: return Opcodes.LDIV; case Type.TYPE_FLOAT: return Opcodes.FDIV; case Type.TYPE_DOUBLE: return Opcodes.DDIV; } break; case '%': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.IMOD; case Type.TYPE_LONG: return Opcodes.LMOD; case Type.TYPE_FLOAT: return Opcodes.FMOD; case Type.TYPE_DOUBLE: return Opcodes.DMOD; } break; case '&': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: case Type.TYPE_BOOL: return Opcodes.IAND; case Type.TYPE_LONG: return Opcodes.LAND; } break; case '|': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: case Type.TYPE_BOOL: return Opcodes.IOR; case Type.TYPE_LONG: return Opcodes.LOR; } break; case '^': switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: case Type.TYPE_BOOL: return Opcodes.IXOR; case Type.TYPE_LONG: return Opcodes.LXOR; } break; case Token.LTLT: switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.ISHL; case Type.TYPE_LONG: return Opcodes.LSHL; } break; case Token.GTGT: switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.ISHR; case Type.TYPE_LONG: return Opcodes.LSHR; } break; case Token.GTGTGT: switch (kind) { case Type.TYPE_BYTE: case Type.TYPE_CHAR: case Type.TYPE_SHORT: case Type.TYPE_INT: return Opcodes.IUSHR; case Type.TYPE_LONG: return Opcodes.LUSHR; } break; } throw new IllegalArgumentException(); } private void writeSwitchInsn(int[][] keySets, Label[] branches, Label defaultBranch) { // computing count of numbers, min, max int count = 0; int min = 0; int max = 0; for (int setIndex = 0; setIndex < keySets.length; setIndex++) { int[] set = keySets[setIndex]; if (setIndex == 0) { min = set[0]; max = set[0]; } for (int j = 0; j < set.length; j++) { int key = set[j]; if (key < min) min = key; else if (key > max) max = key; } count += set.length; } // we choose switch instruction which is shorter int lookupSize = 2 + count*6; int tableSize = 4 + 4 + 2*(max-min+1); if (tableSize < 10) tableSize = Integer.MAX_VALUE; // overflow if (tableSize <= lookupSize) { // TABLESWITCH Label[] jumps = new Label[max-min+1]; for (int i=0; i < jumps.length; i++) { jumps[i] = defaultBranch; } for (int setIndex = 0; setIndex < keySets.length; setIndex++) { int[] set = keySets[setIndex]; for (int i = 0; i < set.length; i++) { jumps[set[i]-min] = branches[setIndex]; } } writer.visitTableSwitch(min, max, defaultBranch, jumps); } else { // LOOKUPSWITCH int[] keys = new int[count]; Label[] jumps = new Label[count]; int ofs = 0; for (int setIndex = 0; setIndex < keySets.length; setIndex++) { int[] set = keySets[setIndex]; for (int j=0; j < set.length; j++) { keys[ofs+j] = set[j]; jumps[ofs+j] = branches[setIndex]; } ofs += set.length; } writer.visitLookupSwitch(defaultBranch, keys, jumps); } } public Object visitApply(ApplyExpr expr, Object args) { if (env.debug) writer.visitLine(expr.lineNumber()); writer.visitLdFunc("Function.apply"); expr.funcExpr.accept(this, args); // create and fill array writer.visitLdcInsn(Int32.toInt32(expr.args.length)); writer.visitInsn(Opcodes.NEWAA); for (int i=0; i<expr.args.length; i++) { writer.visitInsn(Opcodes.DUP); writer.visitLdcInsn(Int32.toInt32(i)); expr.args[i].accept(this, args); writer.visitInsn(Opcodes.AASTORE); } writer.visitCallInsn(Opcodes.CALL, 2); return null; } public Object visitArrayElement(ArrayElementExpr expr, Object args) { expr.arrayExpr.accept(this, args); expr.indexExpr.accept(this, args); int loadInsn = Opcodes.AALOAD; Type arrayType = expr.arrayExpr.returnType(); switch (arrayType.kind) { case Type.TYPE_ARRAY: switch (((ArrayType)arrayType).elementType.kind) { case Type.TYPE_BOOL: loadInsn = Opcodes.ZALOAD; break; case Type.TYPE_BYTE: loadInsn = Opcodes.BALOAD; break; case Type.TYPE_CHAR: loadInsn = Opcodes.CALOAD; break; case Type.TYPE_SHORT: loadInsn = Opcodes.SALOAD; break; case Type.TYPE_INT: loadInsn = Opcodes.IALOAD; break; case Type.TYPE_LONG: loadInsn = Opcodes.LALOAD; break; case Type.TYPE_FLOAT: loadInsn = Opcodes.FALOAD; break; case Type.TYPE_DOUBLE: loadInsn = Opcodes.DALOAD; break; } break; case Type.TYPE_INTRANGE: loadInsn = Opcodes.IALOAD; break; case Type.TYPE_LONGRANGE: loadInsn = Opcodes.LALOAD; break; } writer.visitInsn(loadInsn); return null; } public Object visitArrayLen(ArrayLenExpr expr, Object args) { expr.arrayExpr.accept(this, args); int lenInsn = Opcodes.AALEN; Type arrayType = expr.arrayExpr.returnType(); if (arrayType.kind == Type.TYPE_ARRAY) { switch (((ArrayType)arrayType).elementType.kind) { case Type.TYPE_BOOL: lenInsn = Opcodes.ZALEN; break; case Type.TYPE_BYTE: lenInsn = Opcodes.BALEN; break; case Type.TYPE_CHAR: lenInsn = Opcodes.CALEN; break; case Type.TYPE_SHORT: lenInsn = Opcodes.SALEN; break; case Type.TYPE_INT: lenInsn = Opcodes.IALEN; break; case Type.TYPE_LONG: lenInsn = Opcodes.LALEN; break; case Type.TYPE_FLOAT: lenInsn = Opcodes.FALEN; break; case Type.TYPE_DOUBLE: lenInsn = Opcodes.DALEN; break; } } writer.visitInsn(lenInsn); return null; } public Object visitBinary(BinaryExpr binary, Object args) { binary.lhs.accept(this, args); binary.rhs.accept(this, args); writer.visitInsn(binaryOperatorInsn(binary.lhs.returnType(), binary.operator)); return null; } public Object visitCall(CallExpr fcall, Object args) { boolean knownFunc = fcall.fload.kind == Expr.EXPR_CONST; if (!knownFunc) { fcall.fload.accept(this, args); } for (int i=0; i<fcall.args.length; i++) { fcall.args[i].accept(this, args); } boolean voidResult = fcall.returnType().kind == Type.TYPE_NONE; if (knownFunc) { String sig = ((Function)((ConstExpr)fcall.fload).value).signature; writer.visitCallConstInsn(voidResult ? Opcodes.CALVC : Opcodes.CALLC, fcall.args.length, sig); } else { writer.visitCallInsn(voidResult ? Opcodes.CALV : Opcodes.CALL, fcall.args.length); } return null; } public Object visitCast(CastExpr cast, Object args) { cast.expr.accept(this, args); Type from = cast.expr.returnType(); Type to = cast.returnType(); switch (from.kind) { case Type.TYPE_BYTE: switch (to.kind) { case Type.TYPE_CHAR: writer.visitInsn(Opcodes.I2C); break; case Type.TYPE_LONG: writer.visitInsn(Opcodes.I2L); break; case Type.TYPE_FLOAT: writer.visitInsn(Opcodes.I2F); break; case Type.TYPE_DOUBLE: writer.visitInsn(Opcodes.I2D); break; } break; case Type.TYPE_SHORT: switch (to.kind) { case Type.TYPE_BYTE: writer.visitInsn(Opcodes.I2B); break; case Type.TYPE_CHAR: writer.visitInsn(Opcodes.I2C); break; case Type.TYPE_LONG: writer.visitInsn(Opcodes.I2L); break; case Type.TYPE_FLOAT: writer.visitInsn(Opcodes.I2F); break; case Type.TYPE_DOUBLE: writer.visitInsn(Opcodes.I2D); break; } break; case Type.TYPE_CHAR: case Type.TYPE_INT: switch (to.kind) { case Type.TYPE_BYTE: writer.visitInsn(Opcodes.I2B); break; case Type.TYPE_SHORT: writer.visitInsn(Opcodes.I2S); break; case Type.TYPE_CHAR: writer.visitInsn(Opcodes.I2C); break; case Type.TYPE_LONG: writer.visitInsn(Opcodes.I2L); break; case Type.TYPE_FLOAT: writer.visitInsn(Opcodes.I2F); break; case Type.TYPE_DOUBLE: writer.visitInsn(Opcodes.I2D); break; } break; case Type.TYPE_LONG: switch (to.kind) { case Type.TYPE_BYTE: writer.visitInsn(Opcodes.L2I); writer.visitInsn(Opcodes.I2B); break; case Type.TYPE_SHORT: writer.visitInsn(Opcodes.L2I); writer.visitInsn(Opcodes.I2S); break; case Type.TYPE_CHAR: writer.visitInsn(Opcodes.L2I); writer.visitInsn(Opcodes.I2C); break; case Type.TYPE_INT: writer.visitInsn(Opcodes.L2I); break; case Type.TYPE_FLOAT: writer.visitInsn(Opcodes.L2F); break; case Type.TYPE_DOUBLE: writer.visitInsn(Opcodes.L2D); break; } break; case Type.TYPE_FLOAT: switch (to.kind) { case Type.TYPE_BYTE: writer.visitInsn(Opcodes.F2I); writer.visitInsn(Opcodes.I2B); break; case Type.TYPE_SHORT: writer.visitInsn(Opcodes.F2I); writer.visitInsn(Opcodes.I2S); break; case Type.TYPE_CHAR: writer.visitInsn(Opcodes.F2I); writer.visitInsn(Opcodes.I2C); break; case Type.TYPE_INT: writer.visitInsn(Opcodes.F2I); break; case Type.TYPE_LONG: writer.visitInsn(Opcodes.F2L); break; case Type.TYPE_DOUBLE: writer.visitInsn(Opcodes.F2D); break; } break; case Type.TYPE_DOUBLE: switch (to.kind) { case Type.TYPE_BYTE: writer.visitInsn(Opcodes.D2I); writer.visitInsn(Opcodes.I2B); break; case Type.TYPE_SHORT: writer.visitInsn(Opcodes.D2I); writer.visitInsn(Opcodes.I2S); break; case Type.TYPE_CHAR: writer.visitInsn(Opcodes.D2I); writer.visitInsn(Opcodes.I2C); break; case Type.TYPE_INT: writer.visitInsn(Opcodes.D2I); break; case Type.TYPE_LONG: writer.visitInsn(Opcodes.D2L); break; case Type.TYPE_FLOAT: writer.visitInsn(Opcodes.D2F); break; } break; } return null; } public Object visitComparison(ComparisonExpr cmp, Object args) { Label lfalse = new Label(); Label lafter = new Label(); visitCondition(cmp, lfalse, false); writer.visitLdcInsn(Boolean.TRUE); writer.visitJumpInsn(Opcodes.GOTO, lafter); writer.visitLabel(lfalse); writer.visitLdcInsn(Boolean.FALSE); writer.visitLabel(lafter); return null; } public Object visitConst(ConstExpr cexpr, Object args) { if (env.debug) writer.visitLine(cexpr.lineNumber()); Object obj = cexpr.value; if (obj instanceof Function) { writer.visitLdFunc(((Function)obj).signature); } else { writer.visitLdcInsn(obj); } return null; } public Object visitConcat(ConcatExpr concat, Object args) { int n = concat.exprs.size(); for (int i = 0; i < n; i++) { ((Expr)concat.exprs.get(i)).accept(this, args); } writer.visitConcat(n); return null; } public Object visitIfElse(IfElseExpr ifelse, Object args) { Label lelse = new Label(); Label lafter = new Label(); // writing if condition visitCondition(ifelse.condition, lelse, false); // writing if body ifelse.ifexpr.accept(this, args); // writing else body writer.visitJumpInsn(Opcodes.GOTO, lafter); writer.visitLabel(lelse); ifelse.elseexpr.accept(this, args); writer.visitLabel(lafter); return null; } public Object visitNewArray(NewArrayExpr newarray, Object args) { if (env.debug) writer.visitLine(newarray.lineNumber()); for (int i=0; i<newarray.lengthExprs.length; i++) { newarray.lengthExprs[i].accept(this, args); } Type type = newarray.returnType(); for (int i=0; i<newarray.lengthExprs.length; i++) { type = ((ArrayType)type).elementType; } int typebyte; int insn; switch (type.kind) { case Type.TYPE_BOOL: { typebyte = Arrays.AR_BOOLEAN; insn = Opcodes.NEWZA; break; } case Type.TYPE_BYTE: { typebyte = Arrays.AR_BYTE; insn = Opcodes.NEWBA; break; } case Type.TYPE_CHAR: { typebyte = Arrays.AR_CHAR; insn = Opcodes.NEWCA; break; } case Type.TYPE_SHORT: { typebyte = Arrays.AR_SHORT; insn = Opcodes.NEWSA; break; } case Type.TYPE_INT: { typebyte = Arrays.AR_INT; insn = Opcodes.NEWIA; break; } case Type.TYPE_LONG: { typebyte = Arrays.AR_LONG; insn = Opcodes.NEWLA; break; } case Type.TYPE_FLOAT: { typebyte = Arrays.AR_FLOAT; insn = Opcodes.NEWFA; break; } case Type.TYPE_DOUBLE: { typebyte = Arrays.AR_DOUBLE; insn = Opcodes.NEWDA; break; } default: { typebyte = Arrays.AR_OBJECT; insn = Opcodes.NEWAA; break; } } if (newarray.lengthExprs.length == 1) { writer.visitInsn(insn); } else { writer.visitNewMultiArray(newarray.lengthExprs.length, typebyte); } return null; } public Object visitNewArrayInit(NewArrayInitExpr newarray, Object args) { if (env.debug) writer.visitLine(newarray.lineNumber()); writer.visitLdcInsn(Int32.toInt32(newarray.initializers.length)); Type objType = newarray.returnType(); int newArrayInsn = Opcodes.NEWAA; int storeInsn = Opcodes.AASTORE; if (objType.kind == Type.TYPE_ARRAY) { switch (((ArrayType)objType).elementType.kind) { case Type.TYPE_BOOL: newArrayInsn = Opcodes.NEWZA; storeInsn = Opcodes.ZASTORE; break; case Type.TYPE_BYTE: newArrayInsn = Opcodes.NEWBA; storeInsn = Opcodes.BASTORE; break; case Type.TYPE_CHAR: newArrayInsn = Opcodes.NEWCA; storeInsn = Opcodes.CASTORE; break; case Type.TYPE_SHORT: newArrayInsn = Opcodes.NEWSA; storeInsn = Opcodes.SASTORE; break; case Type.TYPE_INT: newArrayInsn = Opcodes.NEWIA; storeInsn = Opcodes.IASTORE; break; case Type.TYPE_LONG: newArrayInsn = Opcodes.NEWLA; storeInsn = Opcodes.LASTORE; break; case Type.TYPE_FLOAT: newArrayInsn = Opcodes.NEWFA; storeInsn = Opcodes.FASTORE; break; case Type.TYPE_DOUBLE: newArrayInsn = Opcodes.NEWDA; storeInsn = Opcodes.DASTORE; break; } } writer.visitInsn(newArrayInsn); for (int i=0; i<newarray.initializers.length; i++) { Expr e = newarray.initializers[i]; if (e != null) { writer.visitInsn(Opcodes.DUP); writer.visitLdcInsn(Int32.toInt32(i)); e.accept(this, args); writer.visitInsn(storeInsn); } } return null; } public Object visitRange(RangeExpr expr, Object args) { boolean isInt = expr.returnType().kind == Type.TYPE_INTRANGE; writer.visitLdcInsn(Int32.toInt32(2)); writer.visitInsn(isInt ? Opcodes.NEWIA : Opcodes.NEWLA); writer.visitInsn(Opcodes.DUP); writer.visitLdcInsn(Int32.ZERO); expr.fromExpr.accept(this, args); writer.visitInsn(isInt ? Opcodes.IASTORE : Opcodes.LASTORE); writer.visitInsn(Opcodes.DUP); writer.visitLdcInsn(Int32.ONE); expr.toExpr.accept(this, args); writer.visitInsn(isInt ? Opcodes.IASTORE : Opcodes.LASTORE); return null; } public Object visitSequential(SequentialExpr expr, Object args) { for (int vi=0; vi<expr.seqVars.length; vi++) addVar(expr.seqVars[vi]); for (int i=0; i < expr.seqExprs.length; i++) { expr.seqExprs[i].accept(this, args); writer.visitVarInsn(Opcodes.STORE, getVarIndex(expr.seqVars[i])); } expr.lastExpr.accept(this, args); for (int vi=0; vi<expr.seqVars.length; vi++) removeVar(expr.seqVars[vi]); return null; } public Object visitSwitch(SwitchExpr switchExpr, Object args) { // prepare labels Label afterSwitch = new Label(); Label defaultBranch = new Label(); Label[] branches = new Label[switchExpr.keySets.length]; for (int i=0; i < branches.length; i++) { branches[i] = new Label(); } // write switch switchExpr.keyExpr.accept(this, args); writeSwitchInsn(switchExpr.keySets, branches, defaultBranch); for (int i=0; i<branches.length; i++) { Expr expr = switchExpr.exprs[i]; writer.visitLabel(branches[i]); expr.accept(this, args); writer.visitJumpInsn(Opcodes.GOTO, afterSwitch); } writer.visitLabel(defaultBranch); switchExpr.elseExpr.accept(this, args); writer.visitLabel(afterSwitch); return null; } public Object visitTryCatch(TryCatchExpr trycatch, Object args) { Label trystart = new Label(); Label tryend = new Label(); Label aftertry = new Label(); writer.visitLabel(trystart); trycatch.tryExpr.accept(this, args); writer.visitJumpInsn(Opcodes.GOTO, aftertry); writer.visitLabel(tryend); writer.visitTryCatchHandler(trystart, tryend); writer.visitInsn(Opcodes.POP); trycatch.catchExpr.accept(this, args); writer.visitLabel(aftertry); return null; } public Object visitUnary(UnaryExpr unary, Object args) { int typekind = unary.returnType().kind; switch (unary.operator) { case '+': unary.expr.accept(this, args); break; case '-': unary.expr.accept(this, args); switch (typekind) { case Type.TYPE_INT: writer.visitInsn(Opcodes.INEG); break; case Type.TYPE_LONG: writer.visitInsn(Opcodes.LNEG); break; case Type.TYPE_FLOAT: writer.visitInsn(Opcodes.FNEG); break; case Type.TYPE_DOUBLE: writer.visitInsn(Opcodes.DNEG); break; } break; case '~': unary.expr.accept(this, args); if (typekind == Type.TYPE_INT) { writer.visitLdcInsn(Int32.M_ONE); writer.visitInsn(Opcodes.IXOR); } else if (typekind == Type.TYPE_LONG) { writer.visitLdcInsn(new Int64(-1)); writer.visitInsn(Opcodes.LXOR); } break; case '!': writer.visitLdcInsn(Int32.ONE); unary.expr.accept(this, args); writer.visitInsn(Opcodes.ISUB); break; } return null; } public Object visitVar(VarExpr expr, Object args) { Var var = expr.var; if (unit.getVar(var.name) == var) { // global var writer.visitLdcInsn(var.name); if (var.defaultValue != null) { writer.visitLdcInsn(var.defaultValue); writer.visitInsn(Opcodes.GETGLOBALDEF); } else { writer.visitInsn(Opcodes.GETGLOBAL); } } else { // local var writer.visitVarInsn(Opcodes.LOAD, getVarIndex(var)); } return null; } /* STATEMENT VISITING METHODS */ public Object visitArraySetStatement(ArraySetStatement stat, Object args) { stat.arrayExpr.accept(this, args); stat.indexExpr.accept(this, args); stat.assignExpr.accept(this, args); int storeInsn = Opcodes.AASTORE; Type arrayType = stat.arrayExpr.returnType(); if (arrayType.kind == Type.TYPE_ARRAY) { switch (((ArrayType)arrayType).elementType.kind) { case Type.TYPE_BOOL: storeInsn = Opcodes.ZASTORE; break; case Type.TYPE_BYTE: storeInsn = Opcodes.BASTORE; break; case Type.TYPE_CHAR: storeInsn = Opcodes.CASTORE; break; case Type.TYPE_SHORT: storeInsn = Opcodes.SASTORE; break; case Type.TYPE_INT: storeInsn = Opcodes.IASTORE; break; case Type.TYPE_LONG: storeInsn = Opcodes.LASTORE; break; case Type.TYPE_FLOAT: storeInsn = Opcodes.FASTORE; break; case Type.TYPE_DOUBLE: storeInsn = Opcodes.DASTORE; break; } } writer.visitInsn(storeInsn); return null; } public Object visitAssignStatement(AssignStatement stat, Object args) { Var var = stat.var; if (unit.getVar(var.name) == var) { // global var writer.visitLdcInsn(var.name); stat.assignExpr.accept(this, args); writer.visitInsn(Opcodes.SETGLOBAL); } else { // local var stat.assignExpr.accept(this, args); writer.visitVarInsn(Opcodes.STORE, getVarIndex(var)); } return null; } public Object visitBlockStatement(BlockStatement block, Object args) { Object[] varnames = block.vars.keys(); for (int vi=0; vi<varnames.length; vi++) addVar((Var)block.vars.get(varnames[vi])); for (int i=0; i<block.statements.size(); i++) { Statement stat = (Statement)block.statements.get(i); stat.accept(this, args); } for (int vi=0; vi<varnames.length; vi++) removeVar((Var)block.vars.get(varnames[vi])); return null; } public Object visitBreakStatement(BreakStatement stat, Object args) { if (env.debug) writer.visitLine(stat.lineNumber()); writer.visitJumpInsn(Opcodes.GOTO, loopEnd); return null; } public Object visitCompoundAssignStatement(CompoundAssignStatement stat, Object args) { Var var = stat.var; if (unit.getVar(var.name) == var) { // global var writer.visitLdcInsn(var.name); writer.visitInsn(Opcodes.DUP); if (var.defaultValue != null) { writer.visitLdcInsn(var.defaultValue); writer.visitInsn(Opcodes.GETGLOBALDEF); } else { writer.visitInsn(Opcodes.GETGLOBAL); } stat.assignExpr.accept(this, args); writer.visitInsn(binaryOperatorInsn(var.type, Token.getBinaryOperator(stat.assignOperator))); writer.visitInsn(Opcodes.SETGLOBAL); } else { // local var if (stat.assignExpr.kind == Expr.EXPR_CONST && stat.var.type.kind == Type.TYPE_INT && (stat.assignOperator == Token.PLUSEQ || stat.assignOperator == Token.MINUSEQ)) { int incr = ((Int32)((ConstExpr)stat.assignExpr).value).value; if (stat.assignOperator == Token.MINUSEQ) incr = -incr; if (incr >= Byte.MIN_VALUE && incr <= Byte.MAX_VALUE) { if (env.debug) writer.visitLine(stat.lineNumber()); writer.visitIincInsn(getVarIndex(var), incr); return null; } } writer.visitVarInsn(Opcodes.LOAD, getVarIndex(var)); stat.assignExpr.accept(this, args); writer.visitInsn(binaryOperatorInsn(stat.var.type, Token.getBinaryOperator(stat.assignOperator))); writer.visitVarInsn(Opcodes.STORE, getVarIndex(var)); } return null; } public Object visitContinueStatement(ContinueStatement stat, Object args) { if (env.debug) writer.visitLine(stat.lineNumber()); writer.visitJumpInsn(Opcodes.GOTO, loopStart); return null; } public Object visitEmptyStatement(EmptyStatement stat, Object args) { return null; } public Object visitExprStatement(ExprStatement stat, Object args) { stat.expr.accept(this, args); if (stat.expr.returnType().kind != Type.TYPE_NONE) { writer.visitInsn(Opcodes.POP); } return null; } public Object visitForLoopStatement(ForLoopStatement stat, Object args) { Label outerLoopStart = loopStart; Label outerLoopEnd = loopEnd; loopStart = new Label(); loopEnd = new Label(); // at first iteration we jump over increment Label afterIncr = new Label(); writer.visitJumpInsn(Opcodes.GOTO, afterIncr); writer.visitLabel(loopStart); stat.increment.accept(this, args); writer.visitLabel(afterIncr); if (stat.body.kind == Statement.STAT_EMPTY) { visitCondition(stat.condition, loopStart, true); } else { visitCondition(stat.condition, loopEnd, false); stat.body.accept(this, args); writer.visitJumpInsn(Opcodes.GOTO, loopStart); } writer.visitLabel(loopEnd); loopStart = outerLoopStart; loopEnd = outerLoopEnd; return null; } public Object visitIfStatement(IfStatement stat, Object args) { Label elseBranch = new Label(); Label afterIf = new Label(); visitCondition(stat.condition, elseBranch, false); stat.ifstat.accept(this, args); if (stat.elsestat.kind != Statement.STAT_EMPTY && flowContinues(stat)) { writer.visitJumpInsn(Opcodes.GOTO, afterIf); } writer.visitLabel(elseBranch); stat.elsestat.accept(this, args); writer.visitLabel(afterIf); return null; } public Object visitLoopStatement(LoopStatement stat, Object args) { Label outerLoopStart = loopStart; Label outerLoopEnd = loopEnd; loopStart = new Label(); loopEnd = new Label(); writer.visitLabel(loopStart); stat.preBody.accept(this, args); if (stat.postBody.kind == Statement.STAT_EMPTY) { visitCondition(stat.condition, loopStart, true); } else { visitCondition(stat.condition, loopEnd, false); stat.postBody.accept(this, args); writer.visitJumpInsn(Opcodes.GOTO, loopStart); } writer.visitLabel(loopEnd); loopStart = outerLoopStart; loopEnd = outerLoopEnd; return null; } public Object visitReturnStatement(ReturnStatement stat, Object args) { if (stat.expr.kind == Expr.EXPR_CONST && ((ConstExpr)stat.expr).value == Null.NULL) { if (env.debug) writer.visitLine(stat.lineNumber()); writer.visitInsn(Opcodes.RET_NULL); } else { stat.expr.accept(this, args); writer.visitInsn(Opcodes.RETURN); } return null; } public Object visitSwitchStatement(SwitchStatement switchStat, Object args) { // prepare labels Label afterSwitch = new Label(); Label defaultBranch = new Label(); Label[] branches = new Label[switchStat.keySets.length]; for (int i=0; i < branches.length; i++) { branches[i] = new Label(); } // write switch switchStat.keyExpr.accept(this, args); writeSwitchInsn(switchStat.keySets, branches, defaultBranch); for (int i=0; i<branches.length; i++) { Statement stat = switchStat.statements[i]; writer.visitLabel(branches[i]); stat.accept(this, args); // write jump if execution continues and it is not the last branch if (flowContinues(stat) && (i+1 < branches.length || switchStat.elseStat.kind != Statement.STAT_EMPTY)) { writer.visitJumpInsn(Opcodes.GOTO, afterSwitch); } } writer.visitLabel(defaultBranch); switchStat.elseStat.accept(this, args); writer.visitLabel(afterSwitch); return null; } public Object visitThrowStatement(ThrowStatement stat, Object args) { stat.errCodeExpr.accept(this, args); stat.errMsgExpr.accept(this, args); writer.visitInsn(Opcodes.THROW); return null; } public Object visitTryCatchStatement(TryCatchStatement trycatch, Object args) { Label tryStart = new Label(); Label tryEnd = new Label(); Label afterTry = new Label(); writer.visitLabel(tryStart); trycatch.tryStat.accept(this, args); if (flowContinues(trycatch.tryStat)) { writer.visitJumpInsn(Opcodes.GOTO, afterTry); } writer.visitLabel(tryEnd); writer.visitTryCatchHandler(tryStart, tryEnd); if (trycatch.catchVar == null) { writer.visitInsn(Opcodes.POP); } else { addVar(trycatch.catchVar); writer.visitVarInsn(Opcodes.STORE, getVarIndex(trycatch.catchVar)); } trycatch.catchStat.accept(this, args); if (trycatch.catchVar != null) { removeVar(trycatch.catchVar); } writer.visitLabel(afterTry); return null; } }