/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.compiler.assembler; import java.util.Arrays; /** * Basic stack type information tracking, complex control instructions are not supported. */ public class Stack { private static final int MIN_STACK_SIZE = 8; private static final Type OBJECT_TYPE = Types.Object; private static final Type STRING_TYPE = Types.String; private static final Type[] EMPTY_STACK = new Type[0]; private final Variables variables; private Type[] stack; private int sp; public Stack(Variables variables) { this.variables = variables; newStack(); } /** * Return a copy of the current stack. * * @return the current stack */ public final Type[] getStack() { if (sp == 0) { return EMPTY_STACK; } return Arrays.copyOf(stack, sp); } /** * Return the current stack pointer. * * @return the current stack pointer */ public final int getStackPointer() { return sp; } /** * Start exception handler for {@code exception}. * * @param exception * the exception type for the catch handler */ void catchHandler(Type exception) { if (stack == null) { newStack(); } sp = 0; push(exception); } /** * Replace the stack type information with the supplied types. * * @param stack * the new stack */ void setStack(Type[] stack) { assert stack != null; int newSize = Math.max(MIN_STACK_SIZE, Integer.highestOneBit(stack.length) << 1); setStack(Arrays.copyOf(stack, newSize), stack.length); } Type[] getStackDirect() { return stack; } void setStack(Type[] stack, int sp) { this.stack = stack; this.sp = sp; } private void increaseStack(int newSize) { stack = Arrays.copyOf(stack, newSize); } private void newStack() { assert sp == 0 : getStackString(); setStack(new Type[MIN_STACK_SIZE], 0); } private void discardStack() { // assert stack != null; setStack(null, 0); } private String getStackString() { if (stack == null) { return "<null>"; } // Replace [] with {} StringBuilder sb = new StringBuilder(Arrays.toString(getStack())); sb.setCharAt(0, '{'); sb.setCharAt(sb.length() - 1, '}'); return sb.toString(); } private static boolean assertEqualTypes(Type[] stack, int sp, Type[] labelStack, int labelsp) { assert sp == labelsp : String.format("%d != %d (%s, %s)", sp, labelsp, Arrays.toString(stack), Arrays.toString(labelStack)); for (int i = 0; i < sp; ++i) { Type t0 = stack[i], t1 = labelStack[i]; assert t0.equals(t1) : String.format("%s != %s", t0, t1); } return true; } private void alignStack(Type[] stack, int sp, Type[] labelStack, int labelsp) { assert sp == labelsp : String.format("%d != %d (%s, %s)", sp, labelsp, Arrays.toString(stack), Arrays.toString(labelStack)); for (int i = 0; i < sp; ++i) { Type t0 = stack[i], t1 = labelStack[i]; if (!t0.equals(t1)) { assert compatibleTypes(t0, t1); if (t0.getSort() <= Type.Sort.INT) { stack[i] = Type.of(Math.max(Math.max(t0.getSort(), t1.getSort()), Type.Sort.INT)); } else if (t0.getSort() <= Type.Sort.DOUBLE) { stack[i] = Type.DOUBLE_TYPE; } else { stack[i] = intersectionType(t0, t1); } } } } private static boolean compatibleTypes(Type left, Type right) { return left == right || left.getSize() == right.getSize() && left.getSort() < Type.Sort.ARRAY == right.getSort() < Type.Sort.ARRAY; } protected Type intersectionType(Type left, Type right) { return OBJECT_TYPE; } /* stack operations */ protected Type peek() { assert sp > 0 : getStackString(); return stack[sp - 1]; } protected Type peek(int n) { assert n < 0; assert sp + n >= 0 : getStackString(); return stack[sp + n]; } protected Type pop(int size) { assert sp > 0 : getStackString(); Type t = stack[--sp]; assert t.getSize() == size : String.format("%d[%s] != %d, %s", t.getSize(), t, size, getStackString()); return t; } protected void pop(Type type) { assert sp > 0 : getStackString(); Type t = stack[--sp]; assert compatibleTypes(t, type) : String.format("%s != %s, %s", t, type, getStackString()); } protected void push(Type type) { if (sp == stack.length) { increaseStack(sp << 1); } stack[sp++] = type; } /* */ protected void jmp(Jump jump) { Type[] labelStack = jump.stack(); if (labelStack == null) { // label not yet visited => forward jump jump.setStack(getStack()); } else if (jump.isResolved()) { // label already resolved assert assertEqualTypes(stack, sp, labelStack, labelStack.length); } else { // update label stack state alignStack(labelStack, labelStack.length, stack, sp); } } protected void label(Jump jump) { Type[] labelStack = jump.stack(); if (labelStack == null) { // new backward label if (stack == null) { // label after discard stack (goto, return, throw), create new stack (or assert in // debug mode) assert false : "newStack after discard stack"; newStack(); } jump.setStack(getStack()); } else if (stack == null) { // label after discard stack (goto, return, throw), retrieve stack from label setStack(labelStack); } else { // update stack state alignStack(stack, sp, labelStack, labelStack.length); } } /* */ public final void mark(Jump jump) { label(jump); } /* constant value instructions */ public final void anull() { push(OBJECT_TYPE); } public final void aconst() { push(STRING_TYPE); } public final void iconst() { push(Type.INT_TYPE); } public final void lconst() { push(Type.LONG_TYPE); } public final void fconst() { push(Type.FLOAT_TYPE); } public final void dconst() { push(Type.DOUBLE_TYPE); } public final void bipush() { push(Type.BYTE_TYPE); } public final void sipush() { push(Type.SHORT_TYPE); } public final void hconst() { push(Types.MethodHandle); } public final void tconst(Type type) { push(Types.Class); } public final void tconst(MethodTypeDescriptor type) { push(Types.MethodType); } public final void ldc(Object cst) { if (cst instanceof Integer) { push(Type.INT_TYPE); } else if (cst instanceof Byte) { push(Type.BYTE_TYPE); } else if (cst instanceof Character) { push(Type.CHAR_TYPE); } else if (cst instanceof Short) { push(Type.SHORT_TYPE); } else if (cst instanceof Boolean) { push(Type.BOOLEAN_TYPE); } else if (cst instanceof Float) { push(Type.FLOAT_TYPE); } else if (cst instanceof Long) { push(Type.LONG_TYPE); } else if (cst instanceof Double) { push(Type.DOUBLE_TYPE); } else if (cst instanceof String) { push(Types.String); } else { throw new IllegalArgumentException(); } } /* local load instructions */ private void load(int var, Type type) { Type t = variables.getVariable(var); assert compatibleTypes(type, t); push(t); } public final void iload(int var) { load(var, Type.INT_TYPE); } public final void lload(int var) { load(var, Type.LONG_TYPE); } public final void fload(int var) { load(var, Type.FLOAT_TYPE); } public final void dload(int var) { load(var, Type.DOUBLE_TYPE); } public final void aload(int var) { load(var, OBJECT_TYPE); } /* array load instructions */ private void aload(Type type) { pop(Type.INT_TYPE); pop(OBJECT_TYPE); push(type); } public final void iaload() { aload(Type.INT_TYPE); } public final void laload() { aload(Type.LONG_TYPE); } public final void faload() { aload(Type.FLOAT_TYPE); } public final void daload() { aload(Type.DOUBLE_TYPE); } public final void aaload() { aload(OBJECT_TYPE); } public final void baload() { aload(Type.BYTE_TYPE); } public final void caload() { aload(Type.CHAR_TYPE); } public final void saload() { aload(Type.SHORT_TYPE); } /* local store instructions */ private void store(int var, Type type) { Type t = variables.getVariable(var); assert compatibleTypes(type, t); pop(type); } public final void istore(int var) { store(var, Type.INT_TYPE); } public final void lstore(int var) { store(var, Type.LONG_TYPE); } public final void fstore(int var) { store(var, Type.FLOAT_TYPE); } public final void dstore(int var) { store(var, Type.DOUBLE_TYPE); } public final void astore(int var) { store(var, OBJECT_TYPE); } /* array store instructions */ private void astore(Type type) { pop(type); pop(Type.INT_TYPE); pop(OBJECT_TYPE); } public final void iastore() { astore(Type.INT_TYPE); } public final void lastore() { astore(Type.LONG_TYPE); } public final void fastore() { astore(Type.FLOAT_TYPE); } public final void dastore() { astore(Type.DOUBLE_TYPE); } public final void aastore() { astore(OBJECT_TYPE); } public final void bastore() { astore(Type.BYTE_TYPE); } public final void castore() { astore(Type.CHAR_TYPE); } public final void sastore() { astore(Type.SHORT_TYPE); } /* stack instructions */ /** * {@code pop} bytecode instruction. */ public final void pop() { pop(1); } /** * {@code pop2} bytecode instruction. */ public final void pop2() { if (peek().getSize() == 2) { pop(2); } else { pop(1); pop(1); } } /** * {@code dup} bytecode instruction. */ public final void dup() { Type t0 = pop(1); push(t0); push(t0); } /** * {@code dupX1} bytecode instruction. */ public final void dupX1() { Type t0 = pop(1); Type t1 = pop(1); push(t0); push(t1); push(t0); } /** * {@code dupX2} bytecode instruction. */ public final void dupX2() { Type t0 = pop(1); if (peek().getSize() == 2) { Type t1 = pop(2); push(t0); push(t1); push(t0); } else { Type t1 = pop(1); Type t2 = pop(1); push(t0); push(t2); push(t1); push(t0); } } /** * {@code dup2} bytecode instruction. */ public final void dup2() { if (peek().getSize() == 2) { Type t0 = pop(2); push(t0); push(t0); } else { Type t0 = pop(1); Type t1 = pop(1); push(t1); push(t0); push(t1); push(t0); } } /** * {@code dup2X1} bytecode instruction. */ public final void dup2X1() { if (peek().getSize() == 2) { Type t0 = pop(2); Type t2 = pop(1); push(t0); push(t2); push(t0); } else { Type t0 = pop(1); Type t1 = pop(1); Type t2 = pop(1); push(t1); push(t0); push(t2); push(t1); push(t0); } } /** * {@code dup2X2} bytecode instruction. */ public final void dup2X2() { if (peek().getSize() == 2) { Type t0 = pop(2); if (peek().getSize() == 2) { Type t2 = pop(2); push(t0); push(t2); push(t0); } else { Type t2 = pop(1); Type t3 = pop(1); push(t0); push(t3); push(t2); push(t0); } } else { Type t0 = pop(1); Type t1 = pop(1); if (peek().getSize() == 2) { Type t2 = pop(2); push(t1); push(t0); push(t2); push(t1); push(t0); } else { Type t2 = pop(1); Type t3 = pop(1); push(t1); push(t0); push(t3); push(t2); push(t1); push(t0); } } } /** * {@code swap} bytecode instruction. */ public final void swap() { Type t0 = pop(1); Type t1 = pop(1); push(t0); push(t1); } /* math instructions */ private void arithmetic(Type type) { pop(type); pop(type); push(type); } private void neg(Type type) { pop(type); push(type); } private void shift(Type type) { pop(Type.INT_TYPE); pop(type); push(type); } public final void iadd() { arithmetic(Type.INT_TYPE); } public final void ladd() { arithmetic(Type.LONG_TYPE); } public final void fadd() { arithmetic(Type.FLOAT_TYPE); } public final void dadd() { arithmetic(Type.DOUBLE_TYPE); } public final void isub() { arithmetic(Type.INT_TYPE); } public final void lsub() { arithmetic(Type.LONG_TYPE); } public final void fsub() { arithmetic(Type.FLOAT_TYPE); } public final void dsub() { arithmetic(Type.DOUBLE_TYPE); } public final void imul() { arithmetic(Type.INT_TYPE); } public final void lmul() { arithmetic(Type.LONG_TYPE); } public final void fmul() { arithmetic(Type.FLOAT_TYPE); } public final void dmul() { arithmetic(Type.DOUBLE_TYPE); } public final void idiv() { arithmetic(Type.INT_TYPE); } public final void ldiv() { arithmetic(Type.LONG_TYPE); } public final void fdiv() { arithmetic(Type.FLOAT_TYPE); } public final void ddiv() { arithmetic(Type.DOUBLE_TYPE); } public final void irem() { arithmetic(Type.INT_TYPE); } public final void lrem() { arithmetic(Type.LONG_TYPE); } public final void frem() { arithmetic(Type.FLOAT_TYPE); } public final void drem() { arithmetic(Type.DOUBLE_TYPE); } public final void ineg() { neg(Type.INT_TYPE); } public final void lneg() { neg(Type.LONG_TYPE); } public final void fneg() { neg(Type.FLOAT_TYPE); } public final void dneg() { neg(Type.DOUBLE_TYPE); } public final void ishl() { shift(Type.INT_TYPE); } public final void lshl() { shift(Type.LONG_TYPE); } public final void ishr() { shift(Type.INT_TYPE); } public final void lshr() { shift(Type.LONG_TYPE); } public final void iushr() { shift(Type.INT_TYPE); } public final void lushr() { shift(Type.LONG_TYPE); } public final void iand() { arithmetic(Type.INT_TYPE); } public final void land() { arithmetic(Type.LONG_TYPE); } public final void ior() { arithmetic(Type.INT_TYPE); } public final void lor() { arithmetic(Type.LONG_TYPE); } public final void ixor() { arithmetic(Type.INT_TYPE); } public final void lxor() { arithmetic(Type.LONG_TYPE); } /* primitive type cast instructions */ private void cast(Type from, Type to) { pop(from); push(to); } /** * {@code i2l} bytecode instruction. */ public final void i2l() { cast(Type.INT_TYPE, Type.LONG_TYPE); } /** * {@code i2f} bytecode instruction. */ public final void i2f() { cast(Type.INT_TYPE, Type.FLOAT_TYPE); } /** * {@code i2d} bytecode instruction. */ public final void i2d() { cast(Type.INT_TYPE, Type.DOUBLE_TYPE); } /** * {@code l2i} bytecode instruction. */ public final void l2i() { cast(Type.LONG_TYPE, Type.INT_TYPE); } /** * {@code l2f} bytecode instruction. */ public final void l2f() { cast(Type.LONG_TYPE, Type.FLOAT_TYPE); } /** * {@code l2d} bytecode instruction. */ public final void l2d() { cast(Type.LONG_TYPE, Type.DOUBLE_TYPE); } /** * {@code f2i} bytecode instruction. */ public final void f2i() { cast(Type.FLOAT_TYPE, Type.INT_TYPE); } /** * {@code f2l} bytecode instruction. */ public final void f2l() { cast(Type.FLOAT_TYPE, Type.LONG_TYPE); } /** * {@code f2d} bytecode instruction. */ public final void f2d() { cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE); } /** * {@code d2i} bytecode instruction. */ public final void d2i() { cast(Type.DOUBLE_TYPE, Type.INT_TYPE); } /** * {@code d2l} bytecode instruction. */ public final void d2l() { cast(Type.DOUBLE_TYPE, Type.LONG_TYPE); } /** * {@code d2f} bytecode instruction. */ public final void d2f() { cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE); } /** * {@code i2b} bytecode instruction. */ public final void i2b() { cast(Type.INT_TYPE, Type.BYTE_TYPE); } /** * {@code i2c} bytecode instruction. */ public final void i2c() { cast(Type.INT_TYPE, Type.CHAR_TYPE); } /** * {@code i2s} bytecode instruction. */ public final void i2s() { cast(Type.INT_TYPE, Type.SHORT_TYPE); } /* compare instructions */ /** * {@code lcmp} bytecode instruction. */ public final void lcmp() { pop(Type.LONG_TYPE); pop(Type.LONG_TYPE); push(Type.INT_TYPE); } /** * {@code fcmpl} bytecode instruction. */ public final void fcmpl() { pop(Type.FLOAT_TYPE); pop(Type.FLOAT_TYPE); push(Type.INT_TYPE); } /** * {@code fcmpg} bytecode instruction. */ public final void fcmpg() { pop(Type.FLOAT_TYPE); pop(Type.FLOAT_TYPE); push(Type.INT_TYPE); } /** * {@code dcmpl} bytecode instruction. */ public final void dcmpl() { pop(Type.DOUBLE_TYPE); pop(Type.DOUBLE_TYPE); push(Type.INT_TYPE); } /** * {@code dcmpg} bytecode instruction. */ public final void dcmpg() { pop(Type.DOUBLE_TYPE); pop(Type.DOUBLE_TYPE); push(Type.INT_TYPE); } /* jump instructions */ /** * {@code ifeq} bytecode instruction. * * @param jump * jump target */ public final void ifeq(Jump jump) { pop(Type.INT_TYPE); jmp(jump); } /** * {@code ifne} bytecode instruction. * * @param jump * jump target */ public final void ifne(Jump jump) { pop(Type.INT_TYPE); jmp(jump); } /** * {@code iflt} bytecode instruction. * * @param jump * jump target */ public final void iflt(Jump jump) { pop(Type.INT_TYPE); jmp(jump); } /** * {@code ifge} bytecode instruction. * * @param jump * jump target */ public final void ifge(Jump jump) { pop(Type.INT_TYPE); jmp(jump); } /** * {@code ifgt} bytecode instruction. * * @param jump * jump target */ public final void ifgt(Jump jump) { pop(Type.INT_TYPE); jmp(jump); } /** * {@code ifle} bytecode instruction. * * @param jump * jump target */ public final void ifle(Jump jump) { pop(Type.INT_TYPE); jmp(jump); } /** * {@code ificmpeq} bytecode instruction. * * @param jump * jump target */ public final void ificmpeq(Jump jump) { pop(Type.INT_TYPE); pop(Type.INT_TYPE); jmp(jump); } /** * {@code ificmpne} bytecode instruction. * * @param jump * jump target */ public final void ificmpne(Jump jump) { pop(Type.INT_TYPE); pop(Type.INT_TYPE); jmp(jump); } /** * {@code ificmplt} bytecode instruction. * * @param jump * jump target */ public final void ificmplt(Jump jump) { pop(Type.INT_TYPE); pop(Type.INT_TYPE); jmp(jump); } /** * {@code ificmpge} bytecode instruction. * * @param jump * jump target */ public final void ificmpge(Jump jump) { pop(Type.INT_TYPE); pop(Type.INT_TYPE); jmp(jump); } /** * {@code ificmpgt} bytecode instruction. * * @param jump * jump target */ public final void ificmpgt(Jump jump) { pop(Type.INT_TYPE); pop(Type.INT_TYPE); jmp(jump); } /** * {@code ificmple} bytecode instruction. * * @param jump * jump target */ public final void ificmple(Jump jump) { pop(Type.INT_TYPE); pop(Type.INT_TYPE); jmp(jump); } /** * {@code ifacmpeq} bytecode instruction. * * @param jump * jump target */ public final void ifacmpeq(Jump jump) { pop(OBJECT_TYPE); pop(OBJECT_TYPE); jmp(jump); } /** * {@code ifacmpne} bytecode instruction. * * @param jump * jump target */ public final void ifacmpne(Jump jump) { pop(OBJECT_TYPE); pop(OBJECT_TYPE); jmp(jump); } /** * {@code goto} bytecode instruction. * * @param jump * jump target */ public final void goTo(Jump jump) { jmp(jump); discardStack(); } /** * {@code ifnull} bytecode instruction. * * @param jump * jump target */ public final void ifnull(Jump jump) { pop(OBJECT_TYPE); jmp(jump); } /** * {@code ifnonnull} bytecode instruction. * * @param jump * jump target */ public final void ifnonnull(Jump jump) { pop(OBJECT_TYPE); jmp(jump); } /* switch instructions */ private void jmp(Jump... jumps) { for (Jump jump : jumps) { jmp(jump); } } /** * {@code tableswitch} bytecode instruction. * * @param min * minimum entry key * @param max * maximum entry key * @param dflt * default case label * @param jumps * case labels */ public final void tableswitch(int min, int max, Jump dflt, Jump[] jumps) { pop(Type.INT_TYPE); jmp(dflt); jmp(jumps); discardStack(); } /** * {@code lookupswitch} bytecode instruction. * * @param dflt * default case label * @param keys * case entry keys * @param jumps * case labels */ public final void lookupswitch(Jump dflt, int[] keys, Jump[] jumps) { pop(Type.INT_TYPE); jmp(dflt); jmp(jumps); discardStack(); } /* return instructions */ private void areturn(Type type) { if (type.getSort() != Type.Sort.VOID) { pop(type); } assert sp == 0 : String.format("sp=%d, stack=%s", sp, getStackString()); discardStack(); } /** * {@code ireturn} bytecode instruction. */ public final void ireturn() { areturn(Type.INT_TYPE); } /** * {@code lreturn} bytecode instruction. */ public final void lreturn() { areturn(Type.LONG_TYPE); } /** * {@code freturn} bytecode instruction. */ public final void freturn() { areturn(Type.FLOAT_TYPE); } /** * {@code dreturn} bytecode instruction. */ public final void dreturn() { areturn(Type.DOUBLE_TYPE); } /** * {@code areturn} bytecode instruction. */ public final void areturn() { areturn(OBJECT_TYPE); } /** * {@code return} bytecode instruction. */ public final void voidreturn() { areturn(Type.VOID_TYPE); } /* field instructions */ /** * {@code getstatic} bytecode instruction. * * @param desc * the field descriptor */ public final void getstatic(Type desc) { push(desc); } /** * {@code putstatic} bytecode instruction. * * @param desc * the field descriptor */ public final void putstatic(Type desc) { pop(desc); } /** * {@code getfield} bytecode instruction. * * @param owner * field owner * @param desc * the field descriptor */ public final void getfield(Type owner, Type desc) { pop(owner); push(desc); } /** * {@code putfield} bytecode instruction. * * @param owner * field owner * @param desc * the field descriptor */ public final void putfield(Type owner, Type desc) { pop(desc); pop(owner); } /* invoke instructions */ private void invoke(MethodTypeDescriptor desc, boolean consumeThis) { for (int i = desc.parameterCount(); i > 0;) { pop(desc.parameterType(--i)); } if (consumeThis) { pop(OBJECT_TYPE); } Type ret = desc.returnType(); if (ret.getSort() != Type.Sort.VOID) { push(ret); } } /** * {@code invokevirtual} bytecode instruction. * * @param desc * the method descriptor */ public final void invokevirtual(MethodTypeDescriptor desc) { invoke(desc, true); } /** * {@code invokespecial} bytecode instruction. * * @param desc * the method descriptor */ public final void invokespecial(MethodTypeDescriptor desc) { invoke(desc, true); } /** * {@code invokestatic} bytecode instruction. * * @param desc * the method descriptor */ public final void invokestatic(MethodTypeDescriptor desc) { invoke(desc, false); } /** * {@code invokeinterface} bytecode instruction. * * @param desc * the method descriptor */ public final void invokeinterface(MethodTypeDescriptor desc) { invoke(desc, true); } /** * {@code invokedynamic} bytecode instruction. * * @param desc * the method descriptor */ public final void invokedynamic(MethodTypeDescriptor desc) { invoke(desc, false); } /* other instructions */ /** * {@code new} bytecode instruction. * * @param type * the object type */ public final void anew(Type type) { push(type); } /** * {@code newarray} or {@code anewarray} bytecode instruction. * * @param type * the array component type */ public final void newarray(Type type) { pop(Type.INT_TYPE); push(type.asArray()); } /** * {@code arraylength} bytecode instruction. */ public final void arraylength() { pop(OBJECT_TYPE); push(Type.INT_TYPE); } /** * {@code athrow} bytecode instruction. */ public final void athrow() { pop(OBJECT_TYPE); discardStack(); } /** * {@code checkcast} bytecode instruction. * * @param type * the object type */ public final void checkcast(Type type) { pop(OBJECT_TYPE); push(type); } /** * {@code instanceOf} bytecode instruction. * * @param type * the object type */ public final void instanceOf(Type type) { pop(OBJECT_TYPE); push(Type.INT_TYPE); } /** * {@code monitorenter} bytecode instruction. */ public final void monitorenter() { pop(OBJECT_TYPE); } /** * {@code monitorexit} bytecode instruction. */ public final void monitorexit() { pop(OBJECT_TYPE); } /** * {@code multianewarray} bytecode instruction. * * @param type * the array type * @param dims * the array dimensions */ public final void multianewarray(Type type, int dims) { for (int i = dims; i > 0; --i) { pop(Type.INT_TYPE); } push(type); } }