/* -*- Mode: Javascript -*-
 * -*- coding: UTF-8 -*-
 * Copyright (C) 2011 by Artur Ventura
 *
 * File: intrp.js
 * Time-stamp: Fri Jul 15 02:46:27 2011
 *
 * Author: Artur Ventura
 *
 */

DEFOP(AALOAD)
/*
 * ..., array ref, index -> ..., ref value
 */
    var index = OPPOP();
    var arrayref = OPPOP();
    
    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);
    
    OPPUSH(arrayref.value[index]);
ENDDEF

DEFOP(AASTORE)
    var value = OPPOP();
    var index = OPPOP();
    var arrayref = OPPOP();
    
    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);
    
    if (!arrayref.classRef.isAssignable(value.cl)){
        JVM_THROWS_NEW(java.lang.ArrayStoreException);
    }
    
    arrayref.value[index] = value;
ENDDEF
    
DEFOP(ACONST_NULL)
    OPPUSH(null);
ENDDEF

DEFOP(ALOAD)
    var i = READ_NEXT();
    OPPUSH(LOCAL_VAR(i))
ENDDEF
    
DEFALIAS(ALOAD_0)
DEFALIAS(ALOAD_1)
DEFALIAS(ALOAD_2)
DEFALIAS(ALOAD_3)
DEFNOP()
    OPPUSH(LOCAL_VAR(OPCODE - ALOAD_0));
ENDDEF

DEFOP(ANEWARRAY) 
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    var count = OPPOP();
    if (count < 0){
        JVM_THROWS_NEW(java.lang.NegativeArraySizeException);
    }
    var clRef = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    var instance = make1DNativeArray(count,false,xl.jvm.classForName(clRef.jvmClassName));
    OPPUSH(instance);
ENDDEF

DEFOP(ARETURN)
    var objectref = OPPOP();
    return {return_object: objectref}
ENDDEF

DEFOP(ARRAYLENGTH)
    var arrayref = OPPOP();
    if (!(arrayref.primitive == true || arrayref.primitive == false)){
        PANIC("NOT AN ARRAY");
    }
    OPPUSH(arrayref.length);
ENDDEF

DEFOP(ASTORE)
    var objectref = OPPOP();
    LOCAL_VAR(READ_NEXT()) = objectref;
ENDDEF
    
DEFALIAS(ASTORE_0)
DEFALIAS(ASTORE_1)
DEFALIAS(ASTORE_2)
DEFALIAS(ASTORE_3)
DEFNOP()
    var objectref = OPPOP();
    LOCAL_VAR(OPCODE-ASTORE_0) = objectref;
ENDDEF


DEFOP(ATHROW)
    var objectref = OPPOP();
    CHECK_NULL(objectref);
    throw [objectref.classRef.name_ref.str,objectref];
ENDDEF

DEFOP(BALOAD)
    var value = OPPOP();
    var index = OPPOP();
    var arrayref = OPPOP();
    
    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);
    
    arrayref.value[index] = value;
ENDDEF

DEFOP(BIPUSH)
    OPPUSH(READ_NEXT());
ENDDEF
    
DEFOP(CALOAD)
    var index = OPPOP();
    var arrayref = OPPOP();

    OPPUSH(arrayref.value[index]);
ENDDEF

DEFOP(CASTORE)
    var value = OPPOP();
    var index = OPPOP();
    var arrayref = OPPOP();
    
    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);
    
    arrayref.value[index] = value;
ENDDEF
    
DEFOP(CHECKCAST)
    var objectref = OPPOP();
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    var clRef = frame.classRef.constantPool.get((indexbyte1 << 8) | indexbyte2);
    if (objectref.classRef.isAssignable(clRef)){
        OPPUSH(objectref);
    }else{
        JVM_THROWS_NEW(java.lang.ClassCastException);            
    }
ENDDEF
    
DEFOP(D2F)
    var value = OPPOPD();
    TO_FLOAT(value)
ENDDEF

DEFOP(D2I)
    var value = OPPOPD();
    TO_INT(value);
ENDDEF

DEFOP(D2L)
    var value = OPPOPD();
    TO_LONG(value);
ENDDEF

DEFOP(DADD)
    var value1 = OPPOPD();
    var value2 = OPPOPD();
    var result = value1 + value2;
    if (IS_OVERFLOW(result,DOUBLE_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,DOUBLE_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(DALOAD)
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    OPPUSHD(arrayref.value[index]);
ENDDEF

DEFOP(DASTORE)
    var value = OPPOPD();
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    arrayref.value[index] = value;
ENDDEF

DEFALIAS(DCMPG)
DEFALIAS(DCMPL)
DEFNOP()
    var value2 = OPPOPD();
    var value1 = OPPOPD();
    if (isNaN(value1) || isNaN(value2)) { OPPUSH((OPCODE == DCMPG)?1:0)}
    if (value1 > value2){
        OPPUSH(1);
    }else if(value1 == value2){
        OPPUSH(0);
    }else{
        OPPUSH(-1);
    }
ENDDEF

DEFALIAS(DCONST_1)
DEFALIAS(DCONST_0)
DEFNOP()
    OPPUSHD(OPCODE-DCONST_0);
ENDDEF

DEFOP(DDIV)
    var value2 = OPPOPD();
    var value1 = OPPOPD();
    var result = value1/value2;
    
    if (IS_OVERFLOW(result,DOUBLE_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,DOUBLE_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(DLOAD)
    var index = READ_NEXT();
    OPPUSHD(LOCAL_VAR(index));
ENDDEF

DEFALIAS(DLOAD_0)
DEFALIAS(DLOAD_1)
DEFALIAS(DLOAD_2)
DEFALIAS(DLOAD_3)
DEFNOP()
    var index = OPCODE - DLOAD_0;
    OPPUSHD(LOCAL_VAR(index));
ENDDEF

DEFOP(DMUL)
    var value2 = OPPOPD();
    var value1 = OPPOPD();
    var result = value1*value2;
    
    if (IS_OVERFLOW(result,DOUBLE_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,DOUBLE_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(DNEG)
    OPPUSHD(NEG(OPPOPD()));
ENDDEF

DEFOP(DREM)
    var value2 = OPPOPD()
    var value1 = OPPOPD()
    var result = value2 % value1
    
    if (IS_OVERFLOW(result,DOUBLE_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,DOUBLE_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(DRETURN)
    var objectref = OPPOPD()
    return {return_object: objectref}
ENDDEF

DEFOP(DSTORE)
    var index = READ_NEXT();
    var value = OPPOPD();
    LOCAL_VAR(index) = value;
ENDDEF

DEFALIAS(DSTORE_0)
DEFALIAS(DSTORE_1)
DEFALIAS(DSTORE_2)
DEFALIAS(DSTORE_3)
DEFNOP()
    var index = OPCODE - DSTORE_0;
    var value = OPPOPD();
    LOCAL_VAR(index) = value;
ENDDEF

DEFOP(DSUB)
    var value2 = OPPOPD()
    var value1 = OPPOPD()
    var result = value2 % value1

    if (IS_OVERFLOW(result,DOUBLE_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,DOUBLE_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(DUP)
    STACK_MOVE(0,1)
ENDDEF

DEFOP(DUP_X1)
    STACK_MOVE(0,1)
    STACK_MOVE(1,2)
    STACK_MOVE(2,0)
ENDDEF

DEFOP(DUP_X2)
    STACK_MOVE(0,1)
    STACK_MOVE(1,2)
    STACK_MOVE(2,3)
    STACK_MOVE(3,0)
ENDDEF

DEFOP(DUP2)
    STACK_MOVE(0,2)
    STACK_MOVE(1,3)
ENDDEF

DEFOP(DUP2_X1)
    STACK_MOVE(0,2)
    STACK_MOVE(1,3)
    STACK_MOVE(2,4)
    STACK_MOVE(3,0)
    STACK_MOVE(4,1)
ENDDEF

DEFOP(DUP2)
    STACK_MOVE(0, 2);
    STACK_MOVE(1, 3);
    STACK_MOVE(2, 4);
    STACK_MOVE(3, 5);
    STACK_MOVE(4, 0);
    STACK_MOVE(5, 1);
ENDDEF

DEFOP(F2D)
    // No Op, can box in the same space.
ENDDEF

DEFOP(F2I)
    var value = OPPOPD();
    TO_INT(value);
ENDDEF

DEFOP(F2L)
    var value = OPPOPD();
    TO_LONG(value);
ENDDEF

DEFOP(FADD)
    var value1 = OPPOPD();
    var value2 = OPPOPD();
    var result = value1 + value2;
    if (IS_OVERFLOW(result,FLOAT_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,FLOAT_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(FALOAD)
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    OPPUSHD(arrayref.value[index]);
ENDDEF

DEFOP(FASTORE)
    var value = OPPOPD();
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    arrayref.value[index] = value;
ENDDEF

DEFALIAS(FCMPG)
DEFALIAS(FCMPL)
DEFNOP()
    var value2 = OPPOPD();
    var value1 = OPPOPD();
    if (isNaN(value1) || isNaN(value2)) { OPPUSH((OPCODE == FCMPG)?1:0)}
    if (value1 > value2){
        OPPUSH(1);
    }else if(value1 == value2){
        OPPUSH(0);
    }else{
        OPPUSH(-1);
    }
ENDDEF

DEFALIAS(FCONST_1)
DEFALIAS(FCONST_0)
DEFNOP()
    OPPUSHD(OPCODE-FCONST_0);
ENDDEF

DEFOP(FDIV)
    var value2 = OPPOPD();
    var value1 = OPPOPD();
    var result = value1*value2;
    
    if (IS_OVERFLOW(result,FLOAT_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,FLOAT_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(FLOAD)
    var index = READ_NEXT();
    OPPUSHD(LOCAL_VAR(index));
ENDDEF

DEFALIAS(FLOAD_0)
DEFALIAS(FLOAD_1)
DEFALIAS(FLOAD_2)
DEFALIAS(FLOAD_3)
DEFNOP()
    var index = OPCODE - FLOAD_0;
    OPPUSHD(LOCAL_VAR(index));
ENDDEF

DEFOP(FMUL)
    var value2 = OPPOPD();
    var value1 = OPPOPD();
    var result = value1*value2;
    
    if (IS_OVERFLOW(result,FLOAT_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,FLOAT_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(FNEG)
    OPPUSHD(NEG(OPPOPD()));
ENDDEF

DEFOP(FREM)
    var value2 = OPPOPD()
    var value1 = OPPOPD()
    var result = value2 % value1
    
    if (IS_OVERFLOW(result,FLOAT_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,FLOAT_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(FRETURN)
    var objectref = OPPOPD()
    return {return_object: objectref}
ENDDEF

DEFOP(FSTORE)
    var index = READ_NEXT();
    var value = OPPOPD();
    LOCAL_VAR(index) = value;
ENDDEF

DEFALIAS(FSTORE_0)
DEFALIAS(FSTORE_1)
DEFALIAS(FSTORE_2)
DEFALIAS(FSTORE_3)
DEFNOP()
    var index = OPCODE - DLOAD_0;
    var value = OPPOPD();
    LOCAL_VAR(index) = value;
ENDDEF

DEFOP(FLOAT_MAX_VALUE)
    var value2 = OPPOPD()
    var value1 = OPPOPD()
    var result = value2 % value1

    if (IS_OVERFLOW(result,FLOAT_MAX_VALUE)){
        OPPUSHD(POSITIVE_INF);
    }else if(IS_UNDERFLOW(result,FLOAT_MIN_VALUE)){
        OPPUSHD(NEGATIVE_INF);        
    }else{
        OPPUSHD(result);
    }
ENDDEF

DEFOP(GETFIELD)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    var objectref = OPPOP();
    
    CHECK_NULL(objectref)
    var field = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    var fieldSig = field.class_ref.name_ref.str + " " + field.name_and_type_ref.name_ref.str ;
    if (!(fieldSig in objectref)){
        fieldSig = xl.resolveField(field.name_and_type_ref.name_ref.str);
    }
    OPPUSH(objectref[fieldSig]);
    LOG(fieldSig);
ENDDEF

DEFOP(GETSTATIC)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    
    var field = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    var aClass = canonicalName(field.class_ref.name_ref);
    
    //check if static
    OPPUSH(xl.jvm.classForName(aClass)[field.class_ref.name_ref.str + " " + field.name_and_type_ref.name_ref.str]);
ENDDEF

DEFOP(GOTO)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    
    var branchoffset = (branchbyte1 << 8) | branchbyte2;
    PC = PC + branchoffset - 3;
ENDDEF

DEFOP(GOTO_W)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var branchbyte3 = READ_NEXT();
    var branchbyte4 = READ_NEXT();
    
    var branchoffset = (branchbyte1 << 24) | (branchbyte2 << 16) | (branchbyte3 << 8) | branchbyte4;
    PC = PC + branchoffset - 5;
ENDDEF

DEFOP(I2B)
    var value = OPPOP();

    if (IS_OVERFLOW(value,BYTE_MAX_VALUE) || IS_UNDERFLOW(result,BYTE_MIN_VALUE)){
        OPPUSH(BYTE_OVERFLOW(value));
    }else{
        OPPUSH(value);
    }
ENDDEF

DEFOP(I2C)
    var value = OPPOP()
    
    if (IS_OVERFLOW(value,CHAR_MAX_VALUE) || IS_UNDERFLOW(result,CHAR_MIN_VALUE)){
        OPPUSH(CHAR_OVERFLOW(value));
    }else{
        OPPUSH(value);
    }
ENDDEF

DEFOP(I2D)
    OPPUSH(null);
ENDDEF

DEFOP(I2F)
    OPPUSH(null);
ENDDEF

DEFOP(I2L)
    //NOP because it is the same size.
ENDDEF

DEFOP(IADD)
    var value1 = OPPOP();
    var value2 = OPPOP();
    var result = value1 + value2;
    if (IS_OVERFLOW(result,INT_MAX_VALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(IALOAD)
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    OPPUSH(arrayref.value[index]);
ENDDEF

DEFOP(IAND)
    var value1 = OPPOP()
    var value2 = OPPOP()
    var result = value1 & value2
    OPPUSH(result);
ENDDEF

DEFOP(IASTORE)
    var value = OPPOP();
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    arrayref.value[index] = value;
ENDDEF

DEFALIAS(ICONST_M1)
DEFALIAS(ICONST_0)
DEFALIAS(ICONST_1)
DEFALIAS(ICONST_2)
DEFALIAS(ICONST_3)
DEFALIAS(ICONST_4)
DEFALIAS(ICONST_5)
DEFNOP()
    OPPUSH(OPCODE - ICONST_0)
ENDDEF

DEFOP(IDIV)
    var value1 = OPPOP();
    var value2 = OPPOP();
    if(value2 == 0){
        JVM_THROWS_NEW(java.lang.ArithmeticException);
    }
    
    var result = Math.round(value1 / value2);
    OPPUSH(result);
ENDDEF

DEFOP(IF_ACMPEQ)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value1 = OPPOP();
    var value2 = OPPOP();
    if (value1 == value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IF_ACMPNE)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value1 = OPPOP();
    var value2 = OPPOP();
    if (value1 != value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IF_ICMPEQ)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value1 = OPPOP();
    var value2 = OPPOP();
    if (value1 == value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IF_ICMPNE)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value1 = OPPOP();
    var value2 = OPPOP();
    if (value1 != value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IF_ICMPLT)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value2 = OPPOP();
    var value1 = OPPOP();
    if (value1 < value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IF_ICMPLE)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value2 = OPPOP();
    var value1 = OPPOP();
    if (value1 <= value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IF_ICMPGT)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value2 = OPPOP();
    var value1 = OPPOP();
    if (value1 > value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IF_ICMPGE)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value2 = OPPOP();
    var value1 = OPPOP();
    if (value1 >= value2){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFEQ)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value == 0){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFNE)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value != 0){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFLT)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value < 0){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFLE)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value <= 0){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFGT)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value > 0){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFGE)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value >= 0){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFNONNULL)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value != NULL){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IFNULL)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var value = OPPOP();

    if (value != NULL){
        var branchoffset = (branchbyte1 << 8) | branchbyte2;
        PC = PC + branchoffset - 3;
    }
ENDDEF

DEFOP(IINC)
    var index = READ_NEXT();
    var aConst = READ_NEXT();
    LOCAL_VAR(index)++;
ENDDEF

DEFOP(ILOAD)
    var index = READ_NEXT();
    OPPUSH(LOCAL_VAR(index));
ENDDEF

DEFALIAS(ILOAD_0)
DEFALIAS(ILOAD_1)
DEFALIAS(ILOAD_2)
DEFALIAS(ILOAD_3)
DEFNOP()
    OPPUSH(LOCAL_VAR(OPCODE - ILOAD_0));
ENDDEF

DEFOP(IMUL)
    var value1 = OPPOP();
    var value2 = OPPOP();
    var result = value1 * value2;
    if (IS_OVERFLOW(result,INT_MAX_VALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(INEG)
    var value = OPPOP();
    var result = 0 - value;
    if (IS_OVERFLOW(result,INT_MAX_VALUE) || IS_UNDERFLOW(result,INT_MINVALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(INSTANCEOF)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();

    var objectref = OPPOP();
    if (objectref == null){ 
        OPPUSH(0); 
        break; 
    }
    var className = canonicalName(xl.constantPool.get((indexbyte1 << 8) | indexbyte2).name_ref);

    if(objectref["class"].isAssignable(jvm.classForName(className))){
        OPPUSH(1);
    }else{
        OPPUSH(0);
    }
ENDDEF

//###############################
//## INVOKES !
//###############################

DEFOP(INVOKEINTERFACE)
   var indexbyte1 = READ_NEXT();
   var indexbyte2 = READ_NEXT();
   var count = READ_NEXT();
   /* expected 0*/ READ_NEXT();
   var args = [];

   var method = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
   for(var i=0; i<method.descriptor.args.length; i++){
       args.push(OPPOP());
   }
   var objectref = OPPOP();
   
   PANIC("NOT IMPLEMENTED YET");
ENDDEF

DEFOP(INVOKESPECIAL)
   var indexbyte1 = READ_NEXT();
   var indexbyte2 = READ_NEXT();

   var methodDesrc = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
   var cl = xl.jvm.classForName(methodDesrc.class_ref.jvmClassName);
   var methodId = "method " + methodDesrc.name_and_type_ref.name_ref.str +  methodDesrc.name_and_type_ref.descriptor_ref.str
   var method = cl[methodId];
   var args = OPSTACK_MULTIPOP(OPSTACK_LENGTH() - method.descriptor.args.length-1);
   var result;
   if(method.access_flags & ACC_NATIVE){
       PANIC("INVOKESPECIAL NATIVE CALL")
   }else{
       result = method.invoke(args,cl);
   }
   if (result != undefined){
       OPPUSH(result.return_object);
   }
ENDDEF

DEFOP(INVOKESTATIC)
   var indexbyte1 = READ_NEXT();
   var indexbyte2 = READ_NEXT();
   var args = [];

   var methodDesrc = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
   var cl = xl.jvm.classForName(methodDesrc.class_ref.jvmClassName);
   var methodId = "method " + methodDesrc.name_and_type_ref.name_ref.str +  methodDesrc.name_and_type_ref.descriptor_ref.str
   var method = cl[methodId];
   var args = OPSTACK_MULTIPOP(OPSTACK_LENGTH() - method.descriptor.args.length);
   var result;
   if(method.access_flags & ACC_NATIVE){
       var className = canonicalName(cl.this_class.name_ref);
       LOG("Calling " + className + " " + method.name_ref.str + method.descriptor_ref.str)
       LOG("!! NATIVE !!")
       if (xl.jvm.internalJNITable[className] && methodId in xl.jvm.internalJNITable[className]){
           result = {return_object: xl.jvm.internalJNITable[className][methodId].apply(cl,args)};
       }else if (xl.jvm.JNITable[className] && methodId in xl.jvm.JNITable[className]){
           result = {return_object: xl.jvm.JNITable[className][methodId].apply(cl,args)};s
       }else{
           PANIC(methodId + " declared as native but not mapped");
       }
        LOG("Returing from " + className + " " + method.name_ref.str + method.descriptor_ref.str)
   }else{
       result = method.invoke(args,cl);
   }
   if (result != undefined && result.return_object!= undefined){
       OPPUSH(result.return_object);
   }
ENDDEF

DEFOP(INVOKEVIRTUAL)
   var indexbyte1 = READ_NEXT();
   var indexbyte2 = READ_NEXT();
   var args = [];

   var methodDesrc = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
   var cl = xl.jvm.classForName(methodDesrc.class_ref.jvmClassName);
   var methodId = "method " + methodDesrc.name_and_type_ref.name_ref.str +  methodDesrc.name_and_type_ref.descriptor_ref.str
   var method = cl[methodId];
   var args = OPSTACK_MULTIPOP(OPSTACK_LENGTH() - method.descriptor.args.length - 1);
   var result;
   if(method.access_flags & ACC_NATIVE){
       PANIC("INVOKEVIRTUAL NATIVE CALL")
   }else{
       result = method.invoke(args,cl);
   }
   if (result != undefined){
       OPPUSH(result.return_object);
   }
ENDDEF

DEFOP(IOR)
    var value1 = OPPOP();
    var value2 = OPPOP();

    OPPUSH(INT_OVERFLOW(value1 | value2));
ENDDEF

DEFOP(IREM)
    var value1 = OPPOP();
    var value2 = OPPOP();
    if(value2 == 0){
        JVM_THROWS_NEW(java.lang.ArithmeticException);
    }

    var result = value1 - Math.round(value1 / value2) * value2
    if (IS_OVERFLOW(result,INT_MAX_VALUE) || IS_UNDERFLOW(result,INT_MIN_VALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(IRETURN)
    var objectref = OPPOP();
    return {return_object: objectref}
ENDDEF

DEFOP(ISHL)
    var value2 = OPPOP();
    var value1 = OPPOP();
    var result = value1 << (value2 & 0x1f);
    
    if (IS_OVERFLOW(result,INT_MAX_VALUE) || IS_UNDERFLOW(result,INT_MIN_VALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(ISHR)
    var value2 = OPPOP();
    var value1 = OPPOP();
    var result = value1 >> (value2 & 0x1f);

    if (IS_OVERFLOW(result,INT_MAX_VALUE) || IS_UNDERFLOW(result,INT_MIN_VALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(ISTORE)
    var index = READ_NEXT();
    LOCAL_VAR(index) = OPPOP();
ENDDEF

DEFALIAS(ISTORE_0)
DEFALIAS(ISTORE_1)
DEFALIAS(ISTORE_2)
DEFALIAS(ISTORE_3)
DEFNOP()
    LOCAL_VAR(OPCODE - ISTORE_0) = OPPOP();
ENDDEF

DEFOP(ISUB)
    var value2 = OPPOP();
    var value1 = OPPOP();
    var result = value1 - value2;
    if (IS_UNDERFLOW(result,INT_MIN_VALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(IUSHR)
    var value2 = OPPOP();
    var value1 = OPPOP();
    OPPUSH((value1<0)?(value1 >> (value2 & 0x1f)) + (2 << ~s):value1 >> (value2 & 0x1f))
ENDDEF

DEFOP(IXOR)
    var value2 = OPPOP();
    var value1 = OPPOP();
    OPPUSH(INT_OVERFLOW(value1 ^ value2));
ENDDEF

DEFOP(JSR)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    OPPUSH(PC + 1);
    PC = (branchbyte1 << 8) | branchbyte2
ENDDEF

DEFOP(JSR_W)
    var branchbyte1 = READ_NEXT();
    var branchbyte2 = READ_NEXT();
    var branchbyte3 = READ_NEXT();
    var branchbyte4 = READ_NEXT();
    
    OPPUSH(PC + 1);
    PC = (branchbyte1 << 24) | (branchbyte2 << 16) | (branchbyte3 << 8) | branchbyte4
ENDDEF

DEFOP(L2D)
    OPPUSH(null);
ENDDEF

DEFOP(L2F)
    OPPUSH(null);
ENDDEF

DEFOP(L2I)
    var value = OPPOP()
    
    if (IS_OVERFLOW(value,LONG_MAX_VALUE) || IS_UNDERFLOW(result,LONG_MIN_VALUE)){
        OPPUSH(LONG_OVERFLOW(value));
    }else{
        OPPUSH(value);
    }
ENDDEF

DEFOP(LADD)
    var value1 = OPPOP();
    var value2 = OPPOP();
    var result = value1 + value2;
    if (IS_OVERFLOW(result,LONG_MAX_VALUE)){
        OPPUSH(LONG_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(LALOAD)
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    OPPUSH(arrayref.value[index]);
ENDDEF

DEFOP(LAND)
    var value1 = OPPOP()
    var value2 = OPPOP()
    var result = value1 & value2
    OPPUSH(result);
ENDDEF

DEFOP(LASTORE)
    var index = OPPOP();
    var arrayref = OPPOP();
    var value = OPPOP();
    
    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    arrayref.value[index] = value;
ENDDEF

DEFOP(LCMP)
    var value2 = OPPOP();
    var value1 = OPPOP();
    
    if (value1 > value2){
        OPPUSH(1);
    }else if (value1 == value2){
        OPPUSH(0);
    }else{
        OPPUSH(-1)
    }
ENDDEF


DEFALIAS(LCONST_0)
DEFALIAS(LCONST_1)
DEFNOP()
    OPPUSH(OPCODE - LCONST_0)
ENDDEF

DEFOP(LDC)
    var index = READ_NEXT();
    var cstt = xl.constantPool.get(index);
    switch(cstt.id){
        case CONSTANT_Integer:
        case CONSTANT_Float:
            OPPUSH(cstt.value);
            break;
        case CONSTANT_String:
            OPPUSH(xl.jvm.makeInstanceOfStringFromJSSTring(cstt.string_ref.str));
            break;
        default:
            PANIC("What now?");        
            break;
    }
ENDDEF

DEFOP(LDC_W)
    var index1 = READ_NEXT();
    var index2 = READ_NEXT();
    var index = (indexbyte1 << 8) | indexbyte2
    var cstt = xl.constantPool.get(index);
    switch(cstt.id){
        case CONSTANT_Integer:
            OPPUSHD(cstt.value);
            break;
        case CONSTANT_Float:
            OPPUSH(cstt.value);
            break;
        case CONSTANT_String:
            PANIC("NEW STRING FROM CONSTANT POOL NOT IMPLEMENTED");
            break;
        default:
            PANIC("What now?");        
    }
ENDDEF

DEFOP(LDC2_W)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    var index = (indexbyte1 << 8) | indexbyte2
    var cstt = xl.constantPool.get(index);
    OPPUSHD(cstt.value);
ENDDEF

DEFOP(LDIV)
    var value1 = OPPOP();
    var value2 = OPPOP();
    if(value2 == 0){
        JVM_THROWS_NEW(java.lang.ArithmeticException);
    }
    
    var result = Math.round(value1 / value2);
    OPPUSH(result);
ENDDEF

DEFOP(LLOAD)
    var index = READ_NEXT();
    OPPUSH(LOCAL_VAR(index));
ENDDEF

DEFALIAS(LLOAD_0)
DEFALIAS(LLOAD_1)
DEFALIAS(LLOAD_2)
DEFALIAS(LLOAD_3)
DEFNOP()
    OPPUSH(OPCODE - LLOAD_0);
ENDDEF

DEFOP(LMUL)
    var value1 = OPPOP();
    var value2 = OPPOP();
    var result = value1 * value2;
    if (IS_OVERFLOW(result,INT_MAX_VALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(LNEG)
    var value = OPPOP();
    var result = 0 - value;
    if (IS_OVERFLOW(result,INT_MAX_VALUE) || IS_UNDERFLOW(result,INT_MINVALUE)){
        OPPUSH(INT_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(LOOKUPSWITCH)
    var shift = PC % 4;
    var instrPC = PC - 1
    if (shift != 0){
        switch(shift){
            case 1:
            READ_NEXT();
            case 2:
            READ_NEXT();
            case 3:
            READ_NEXT();
        }
    }
    var stack_key = OPPOP();
    
    var defaultbyte1 = READ_NEXT();
    var defaultbyte2 = READ_NEXT();
    var defaultbyte3 = READ_NEXT();
    var defaultbyte4 = READ_NEXT();
    var jmp = QWORD(defaultbyte1,defaultbyte2,defaultbyte3,defaultbyte4);
    
    var npair1 = READ_NEXT();
    var npair2 = READ_NEXT();
    var npair3 = READ_NEXT();
    var npair4 = READ_NEXT();
    var npair = QWORD(npair1,npair2,npair3,npair4);

    for(var i=0; i<npair; i++){
        var key1 = READ_NEXT();
        var key2 = READ_NEXT();
        var key3 = READ_NEXT();
        var key4 = READ_NEXT();
        var key = QWORD(key1,key2,key3,key4);
        
        var value1 = READ_NEXT();
        var value2 = READ_NEXT();
        var value3 = READ_NEXT();
        var value4 = READ_NEXT();
        var value = QWORD(value1,value2,value3,value4);
        
        if(key == stack_key){
            jmp = instrPC + value;
        }
    }
    PC = instrPC + jmp;
ENDDEF

DEFOP(LOR)
    var value1 = OPPOP();
    var value2 = OPPOP();

    OPPUSH(LONG_OVERFLOW(value1 | value2));
ENDDEF

DEFOP(LREM)
    var value1 = OPPOP();
    var value2 = OPPOP();
    if(value2 == 0){
        JVM_THROWS_NEW(java.lang.ArithmeticException);
    }

    var result = value1 - Math.round(value1 / value2) * value2
    if (IS_OVERFLOW(result,LONG_MAX_VALUE) || IS_UNDERFLOW(result,LONG_MIN_VALUE)){
        OPPUSH(LONG_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(LRETURN)
    var objectref = OPPOP();
    return {return_object: objectref}
ENDDEF

DEFOP(LSHL)
    var value2 = OPPOP();
    var value1 = OPPOP();
    var result = value1 << (value2 & 0x1f);
    
    if (IS_OVERFLOW(result,LONG_MAX_VALUE) || IS_UNDERFLOW(result,LONG_MIN_VALUE)){
        OPPUSH(LONG_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(LSHR)
    var value2 = OPPOP();
    var value1 = OPPOP();
    var result = value1 >> (value2 & 0x1f);

    if (IS_OVERFLOW(result,LONG_MAX_VALUE) || IS_UNDERFLOW(result,LONG_MIN_VALUE)){
        OPPUSH(LONG_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(LSTORE)
    var index = READ_NEXT();
    LOCAL_VAR(index) = OPPOP();
ENDDEF

DEFALIAS(LSTORE_0)
DEFALIAS(LSTORE_1)
DEFALIAS(LSTORE_2)
DEFALIAS(LSTORE_3)
DEFNOP()
    LOCAL_VAR(READ_NEXT()) = (OPCODE - LSTORE_0);
ENDDEF

DEFOP(LSUB)
    var value1 = OPPOP();
    var value2 = OPPOP();
    var result = value1 - value2;
    if (IS_UNDERFLOW(result,LONG_MAX_VALUE)){
        OPPUSH(LONG_OVERFLOW(result));
    }else{
        OPPUSH(result);
    }
ENDDEF

DEFOP(LUSHR)
    var value2 = OPPOP();
    var value1 = OPPOP();
    OPPUSH((value1<0)?(value1 >> (value2 & 0x1f)) + (2 << ~s):value1 >> (value2 & 0x1f))
ENDDEF

DEFOP(LXOR)
    var value2 = OPPOP();
    var value1 = OPPOP();
    OPPUSH(LONG_OVERFLOW(value1 ^ value2));
ENDDEF

DEFOP(MONITORENTER)
    PANIC("THREADING NOT IMPLEMENTED YET");
ENDDEF

DEFOP(MONITOREXIT)
    PANIC("THREADING NOT IMPLEMENTED YET");
ENDDEF

DEFOP(MULTIANEWARRAY)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    var dimensions = READ_NEXT();
    var counts = [];
    for(var i=0; i<dimensions; i++){
        counts.add(OPPOP());
    }
    var clRef = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    var instance = {length:counts, dimensions:dimensions, value:[], 'class':xl.jvm.classForName(clRef)};
    OPPUSH(instance);
ENDDEF

DEFOP(NEW)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    
    var clRef = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    var newInstance = xl.jvm.classForName(clRef.jvmClassName).makeInstance();
    OPPUSH(newInstance);
ENDDEF

DEFOP(NOP)
ENDDEF

DEFOP(NEWARRAY)
    var atype = READ_NEXT();
    var count = OPPOP();
    if (count < 0){
        JVM_THROWS_NEW(java.lang.NegativeArraySizeException);
    }
    var clRef = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    var instance = make1DNativeArray(count,true,atype);
    OPPUSH(instance);
ENDDEF

DEFOP(POP)
    OPPOP();
ENDDEF

DEFOP(POP2)
    OPPOP();
    OPPOP();
ENDDEF

DEFOP(PUTFIELD)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    var value = OPPOP();
    var objectref = OPPOP();
    
    CHECK_NULL(objectref)
    var field = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    //check if static
    objectref[field.class_ref.name_ref.str + " " + field.name_and_type_ref.name_ref.str] = value;
ENDDEF

DEFOP(PUTSTATIC)
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    var value = OPPOP();
    
    var field = xl.constantPool.get((indexbyte1 << 8) | indexbyte2);
    var aClass = field.class_ref.jvmClassName;
    
    //check if static
    xl.jvm.classForName(aClass)[canonicalName(field.class_ref.name_ref) + " " + field.name_and_type_ref.name_ref.str] = value;
ENDDEF

DEFOP(RET)
    var index = READ_NEXT();
    PC = LOCAL_VAR(index);
ENDDEF

DEFOP(RETURN)
    return;
ENDDEF

DEFOP(SALOAD)
    var index = OPPOP();
    var arrayref = OPPOP();

    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    OPPUSH(arrayref.value[index]);
ENDDEF

DEFOP(SASTORE)
    var index = OPPOP();
    var arrayref = OPPOP();
    var value = OPPOP();
    
    CHECK_NULL(arrayref);
    CHECK_ARRAY_INDEX(index,arrayref);

    arrayref.value[index] = value;
ENDDEF

DEFOP(SIPUSH)
    var byte1 = READ_NEXT();
    var byte2 = READ_NEXT();
    OPPUSH((byte1 << 8) | byte2);
ENDDEF

DEFOP(SWAP)
    var value1 = OPPOP();
    var value2 = OPPOP();
    OPPUSH(value2); OPPUSH(value1);
ENDDEF

DEFOP(TABLESWITCH)
    var shift = PC % 4;
    var instrPC = PC - 1
    if (shift != 0){
        switch(shift){
            case 1:
            READ_NEXT();
            case 2:
            READ_NEXT();
            case 3:
            READ_NEXT();
        }
    }
    var stack_key = OPPOP();
    
    var defaultbyte1 = READ_NEXT();
    var defaultbyte2 = READ_NEXT();
    var defaultbyte3 = READ_NEXT();
    var defaultbyte4 = READ_NEXT();
    var defaultbyte = QWORD(defaultbyte1,defaultbyte2,defaultbyte3,defaultbyte4);
    
    var low1 = READ_NEXT();
    var low2 = READ_NEXT();
    var low3 = READ_NEXT();
    var low4 = READ_NEXT();
    var low= QWORD(low1,low2,low3,low4);
    
    var high1 = READ_NEXT();
    var high2 = READ_NEXT();
    var high3 = READ_NEXT();
    var high4 = READ_NEXT();
    var high= QWORD(high1,high2,high3,high4);
    

    var index = OPPOP();
    var jmp = defaultbyte;

        

    if (index < low || index > high){
        var length = high - low + 2;    
        for(var i=0; i<length; i++){
            var jump1 = READ_NEXT();
            var jump2 = READ_NEXT();
            var jump3 = READ_NEXT();
            var jump4 = READ_NEXT();
            var jump = QWORD(jump1,jump2,jump3,jump4);
            
            if (index-low == i){
                jmp = jump;
            }
        }
    }
    PC = instrPC + jmp;
ENDDEF

DEFOP(WIDE)
    var op = READ_NEXT();
    var indexbyte1 = READ_NEXT();
    var indexbyte2 = READ_NEXT();
    switch(op){
        case ILOAD:
        case ALOAD:
        case LLOAD:
            OPPUSH(LOCAL_VAR((indexbyte1 << 8) | indexbyte2));
            break;
        case FLOAD:
        case DLOAD:
            OPPUSHD(LOCAL_VAR((indexbyte1 << 8) | indexbyte2));
            break;
        case ISTORE:
        case ASTORE:
        case LSTORE:
            LOCAL_VAR((indexbyte1 << 8) | indexbyte2) = OPPOP();
            break;
        case FSTORE:
        case DSTORE:
            LOCAL_VAR((indexbyte1 << 8) | indexbyte2) = OPPOPD();
            break;
    }
ENDDEF