/*
* 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.evm;
import alchemy.system.AlchemyException;
import alchemy.system.Function;
import alchemy.system.Library;
import alchemy.system.Process;
import alchemy.system.ProcessKilledException;
import alchemy.types.Float32;
import alchemy.types.Float64;
import alchemy.types.Int32;
import alchemy.types.Int64;
import alchemy.util.Arrays;
import alchemy.util.Strings;
/**
* Ether Virtual Machine.
* TODO: switch to the register based machine already.
*
* @author Sergey Basalaev
*/
final class EtherFunction extends Function {
private final int stacksize;
private final int localsize;
private final byte[] bcode;
private final char[] dbgtable;
private final char[] errtable;
private final Object[] cpool;
EtherFunction(Library owner, String funcname, Object[] cpool, int stacksize, int localsize, byte[] code, char[] dbgtable, char[] errtable) {
super(owner, funcname);
this.stacksize = stacksize;
this.localsize = localsize;
this.bcode = code;
this.cpool = cpool;
this.dbgtable = dbgtable;
this.errtable = errtable;
}
public Object invoke(Process p, Object[] args) throws AlchemyException, ProcessKilledException {
//initializing
final Object[] stack = new Object[localsize+stacksize];
System.arraycopy(args, 0, stack, 0, args.length);
int head = localsize-1;
final byte[] code = this.bcode;
int ct = 0;
while (!p.killed) {
try {
int instr = code[ct];
ct++;
switch (instr) {
// CONSTANTS
case Opcodes.ACONST_NULL: {
head++;
stack[head] = null;
break;
}
case Opcodes.ICONST_M1: {
head++;
stack[head] = Int32.M_ONE;
break;
}
case Opcodes.ICONST_0: {
head++;
stack[head] = Int32.ZERO;
break;
}
case Opcodes.ICONST_1: {
head++;
stack[head] = Int32.ONE;
break;
}
case Opcodes.ICONST_2: {
head++;
stack[head] = Int32.toInt32(2);
break;
}
case Opcodes.ICONST_3: {
head++;
stack[head] = Int32.toInt32(3);
break;
}
case Opcodes.ICONST_4: {
head++;
stack[head] = Int32.toInt32(4);
break;
}
case Opcodes.ICONST_5: {
head++;
stack[head] = Int32.toInt32(5);
break;
}
case Opcodes.LCONST_0: {
head++;
stack[head] = new Int64(0L);
break;
}
case Opcodes.LCONST_1: {
head++;
stack[head] = new Int64(1L);
break;
}
case Opcodes.FCONST_0: {
head++;
stack[head] = new Float32(0f);
break;
}
case Opcodes.FCONST_1: {
head++;
stack[head] = new Float32(1f);
break;
}
case Opcodes.FCONST_2: {
head++;
stack[head] = new Float32(2f);
break;
}
case Opcodes.DCONST_0: {
head++;
stack[head] = new Float64(0d);
break;
}
case Opcodes.DCONST_1: {
head++;
stack[head] = new Float64(1d);
break;
}
//CONVERSIONS
case Opcodes.I2L: {
stack[head] = new Int64(((Int32)(stack[head])).value);
break;
}
case Opcodes.I2F: {
stack[head] = new Float32(((Int32)stack[head]).value);
break;
}
case Opcodes.I2D: {
stack[head] = new Float64(((Int32)stack[head]).value);
break;
}
case Opcodes.L2F: {
stack[head] = new Float32(((Int64)stack[head]).value);
break;
}
case Opcodes.L2D: {
stack[head] = new Float64(((Int64)stack[head]).value);
break;
}
case Opcodes.L2I: {
stack[head] = Int32.toInt32((int)((Int64)stack[head]).value);
break;
}
case Opcodes.F2D: {
stack[head] = new Float64(((Float32)stack[head]).value);
break;
}
case Opcodes.F2I: {
stack[head] = Int32.toInt32((int)((Float32)stack[head]).value);
break;
}
case Opcodes.F2L: {
stack[head] = new Int64((long)((Float32)stack[head]).value);
break;
}
case Opcodes.D2I: {
stack[head] = Int32.toInt32((int)((Float64)stack[head]).value);
break;
}
case Opcodes.D2L: {
stack[head] = new Int64((long)((Float64)stack[head]).value);
break;
}
case Opcodes.D2F: {
stack[head] = new Float32((float)((Float64)stack[head]).value);
break;
}
case Opcodes.I2C: {
stack[head] = Int32.toInt32((char)((Int32)stack[head]).value);
break;
}
case Opcodes.I2B: {
stack[head] = Int32.toInt32((byte)((Int32)stack[head]).value);
break;
}
case Opcodes.I2S: {
stack[head] = Int32.toInt32((short)((Int32)stack[head]).value);
break;
}
//INTEGER ARITHMETICS
case Opcodes.IADD: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value + ((Int32)stack[head+1]).value);
break;
}
case Opcodes.ISUB: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value - ((Int32)stack[head+1]).value);
break;
}
case Opcodes.IMUL: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value * ((Int32)stack[head+1]).value);
break;
}
case Opcodes.IDIV: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value / ((Int32)stack[head+1]).value);
break;
}
case Opcodes.IMOD: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value % ((Int32)stack[head+1]).value);
break;
}
case Opcodes.INEG: {
stack[head] = Int32.toInt32(-((Int32)stack[head]).value);
break;
}
case Opcodes.ICMP: {
head--;
int itmp = ((Int32)stack[head]).value - ((Int32)stack[head+1]).value;
stack[head] = (itmp > 0) ? Int32.ONE : (itmp == 0 ? Int32.ZERO : Int32.M_ONE);
break;
}
case Opcodes.ISHL: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value << ((Int32)stack[head+1]).value);
break;
}
case Opcodes.ISHR: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value >> ((Int32)stack[head+1]).value);
break;
}
case Opcodes.IUSHR: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value >>> ((Int32)stack[head+1]).value);
break;
}
case Opcodes.IAND: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value & ((Int32)stack[head+1]).value);
break;
}
case Opcodes.IOR: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value | ((Int32)stack[head+1]).value);
break;
}
case Opcodes.IXOR: {
head--;
stack[head] = Int32.toInt32(((Int32)stack[head]).value ^ ((Int32)stack[head+1]).value);
break;
}
//LONG ARITHMETICS
case Opcodes.LADD: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value + ((Int64)stack[head+1]).value);
break;
}
case Opcodes.LSUB: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value - ((Int64)stack[head+1]).value);
break;
}
case Opcodes.LMUL: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value * ((Int64)stack[head+1]).value);
break;
}
case Opcodes.LDIV: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value / ((Int64)stack[head+1]).value);
break;
}
case Opcodes.LMOD: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value % ((Int64)stack[head+1]).value);
break;
}
case Opcodes.LNEG: {
stack[head] = new Int64(-((Int64)stack[head]).value);
break;
}
case Opcodes.LCMP: {
head--;
long ltmp = ((Int64)stack[head]).value - ((Int64)stack[head+1]).value;
stack[head] = (ltmp > 0L) ? Int32.ONE : (ltmp == 0L ? Int32.ZERO : Int32.M_ONE);
break;
}
case Opcodes.LSHL: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value << ((Int32)stack[head+1]).value);
break;
}
case Opcodes.LSHR: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value >> ((Int32)stack[head+1]).value);
break;
}
case Opcodes.LUSHR: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value >>> ((Int32)stack[head+1]).value);
break;
}
case Opcodes.LAND: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value & ((Int64)stack[head+1]).value);
break;
}
case Opcodes.LOR: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value | ((Int64)stack[head+1]).value);
break;
}
case Opcodes.LXOR: {
head--;
stack[head] = new Int64(((Int64)stack[head]).value ^ ((Int64)stack[head+1]).value);
break;
}
//FLOAT ARITHMETICS
case Opcodes.FADD: {
head--;
stack[head] = new Float32(((Float32)stack[head]).value + ((Float32)stack[head+1]).value);
break;
}
case Opcodes.FSUB: {
head--;
stack[head] = new Float32(((Float32)stack[head]).value - ((Float32)stack[head+1]).value);
break;
}
case Opcodes.FMUL: {
head--;
stack[head] = new Float32(((Float32)stack[head]).value * ((Float32)stack[head+1]).value);
break;
}
case Opcodes.FDIV: {
head--;
stack[head] = new Float32(((Float32)stack[head]).value / ((Float32)stack[head+1]).value);
break;
}
case Opcodes.FMOD: {
head--;
stack[head] = new Float32(((Float32)stack[head]).value % ((Float32)stack[head+1]).value);
break;
}
case Opcodes.FNEG: {
stack[head] = new Float32(-((Float32)stack[head]).value);
break;
}
case Opcodes.FCMP: {
head--;
float ftmp = ((Float32)stack[head]).value - ((Float32)stack[head+1]).value;
stack[head] = (ftmp > 0) ? Int32.ONE : (ftmp == 0 ? Int32.ZERO : Int32.M_ONE);
break;
}
//DOUBLE ARITHMETICS
case Opcodes.DADD: {
head--;
stack[head] = new Float64(((Float64)stack[head]).value + ((Float64)stack[head+1]).value);
break;
}
case Opcodes.DSUB: {
head--;
stack[head] = new Float64(((Float64)stack[head]).value - ((Float64)stack[head+1]).value);
break;
}
case Opcodes.DMUL: {
head--;
stack[head] = new Float64(((Float64)stack[head]).value * ((Float64)stack[head+1]).value);
break;
}
case Opcodes.DDIV: {
head--;
stack[head] = new Float64(((Float64)stack[head]).value / ((Float64)stack[head+1]).value);
break;
}
case Opcodes.DMOD: {
head--;
stack[head] = new Float64(((Float64)stack[head]).value % ((Float64)stack[head+1]).value);
break;
}
case Opcodes.DNEG: {
stack[head] = new Float64(-((Float64)stack[head]).value);
break;
}
case Opcodes.DCMP: {
head--;
double dtmp = ((Float64)stack[head]).value - ((Float64)(stack[head+1])).value;
stack[head] = (dtmp > 0) ? Int32.ONE : (dtmp == 0 ? Int32.ZERO : Int32.M_ONE);
break;
}
//LOCALS LOADERS AND SAVERS
case Opcodes.LOAD_0:
case Opcodes.LOAD_1:
case Opcodes.LOAD_2:
case Opcodes.LOAD_3:
case Opcodes.LOAD_4:
case Opcodes.LOAD_5:
case Opcodes.LOAD_6:
case Opcodes.LOAD_7: {
head++;
stack[head] = stack[instr & 7];
break;
}
case Opcodes.LOAD: { //load <ubyte>
head++;
stack[head] = stack[code[ct] & 0xff];
ct++;
break;
}
//variable savers
case Opcodes.STORE_0:
case Opcodes.STORE_1:
case Opcodes.STORE_2:
case Opcodes.STORE_3:
case Opcodes.STORE_4:
case Opcodes.STORE_5:
case Opcodes.STORE_6:
case Opcodes.STORE_7: {
stack[instr & 7] = stack[head];
head--;
break;
}
case Opcodes.STORE: { //store <ubyte>
stack[code[ct] & 0xff] = stack[head];
ct++;
head--;
break;
}
//GLOBALS LOADERS AND SAVERS
case Opcodes.GETGLOBAL: {
stack[head] = p.getGlobal(library, (String)stack[head], null);
break;
}
case Opcodes.GETGLOBALDEF: {
head--;
stack[head] = p.getGlobal(library, (String)stack[head], stack[head+1]);
break;
}
case Opcodes.SETGLOBAL: {
p.setGlobal(library, (String)stack[head-1], stack[head]);
head -= 2;
break;
}
//BRANCHING
case Opcodes.IFEQ: { //ifeq <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head]).value == 0) ct = itmp;
head--;
break;
}
case Opcodes.IFNE: { //ifne <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head]).value != 0) ct = itmp;
head--;
break;
}
case Opcodes.IFLT: { //iflt <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head]).value < 0) ct = itmp;
head--;
break;
}
case Opcodes.IFGE: { //ifge <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head]).value >= 0) ct = itmp;
head--;
break;
}
case Opcodes.IFGT: { //ifgt <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head]).value > 0) ct = itmp;
head--;
break;
}
case Opcodes.IFLE: { //ifle <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head]).value <= 0) ct = itmp;
head--;
break;
}
case Opcodes.GOTO: { //goto <ushort>
ct = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
break;
}
case Opcodes.IFNULL: { //ifnull <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (stack[head] == null) ct = itmp;
head--;
break;
}
case Opcodes.IFNNULL: { //ifnnull <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (stack[head] != null) ct = itmp;
head--;
break;
}
case Opcodes.IF_ICMPLT: { //if_icmplt <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head-1]).value < ((Int32)stack[head]).value) ct = itmp;
head -= 2;
break;
}
case Opcodes.IF_ICMPGE: { //if_icmpge <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head-1]).value >= ((Int32)stack[head]).value) ct = itmp;
head -= 2;
break;
}
case Opcodes.IF_ICMPGT: { //if_icmpgt <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head-1]).value > ((Int32)stack[head]).value) ct = itmp;
head -= 2;
break;
}
case Opcodes.IF_ICMPLE: { //if_icmple <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (((Int32)stack[head-1]).value <= ((Int32)stack[head]).value) ct = itmp;
head -= 2;
break;
}
case Opcodes.IF_ACMPEQ: { //if_acmpeq <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (stack[head-1] != null
? stack[head-1].equals(stack[head])
: stack[head] == null) ct = itmp;
head -= 2;
break;
}
case Opcodes.IF_ACMPNE: { //if_acmpne <ushort>
int itmp = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
ct += 2;
if (stack[head-1] != null
? !stack[head-1].equals(stack[head])
: stack[head] != null) ct = itmp;
head -= 2;
break;
}
case Opcodes.JSR: { //jsr <ushort>
head++;
stack[head] = Int32.toInt32(ct+2);
ct = (code[ct] & 0xff) << 8 | (code[ct+1] & 0xff);
break;
}
case Opcodes.RET: { //ret
ct = ((Int32)stack[head]).value;
head--;
break;
}
//FUNCTION CALLS
case Opcodes.CALL_0:
case Opcodes.CALL_1:
case Opcodes.CALL_2:
case Opcodes.CALL_3:
case Opcodes.CALL_4:
case Opcodes.CALL_5:
case Opcodes.CALL_6:
case Opcodes.CALL_7:
case Opcodes.CALV_0:
case Opcodes.CALV_1:
case Opcodes.CALV_2:
case Opcodes.CALV_3:
case Opcodes.CALV_4:
case Opcodes.CALV_5:
case Opcodes.CALV_6:
case Opcodes.CALV_7: {
int paramlen = instr & 7;
Object[] params = new Object[paramlen];
head -= paramlen;
System.arraycopy(stack, head+1, params, 0, paramlen);
stack[head] = ((Function)stack[head]).invoke(p, params);
if ((instr & 8) != 0) head--;
break;
}
case Opcodes.CALLC_0:
case Opcodes.CALLC_1:
case Opcodes.CALLC_2:
case Opcodes.CALLC_3:
case Opcodes.CALLC_4:
case Opcodes.CALLC_5:
case Opcodes.CALLC_6:
case Opcodes.CALLC_7:
case Opcodes.CALVC_0:
case Opcodes.CALVC_1:
case Opcodes.CALVC_2:
case Opcodes.CALVC_3:
case Opcodes.CALVC_4:
case Opcodes.CALVC_5:
case Opcodes.CALVC_6:
case Opcodes.CALVC_7: { // cal?c_? <ushort>
int paramlen = instr & 7;
Object[] params = new Object[paramlen];
head -= paramlen-1;
System.arraycopy(stack, head, params, 0, paramlen);
Function f = (Function) cpool[((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff)];
ct += 2;
stack[head] = f.invoke(p, params);
if ((instr & 8) != 0) head--;
break;
}
case Opcodes.CALL: {//call <ubyte>
int paramlen = code[ct] & 0xff;
ct++;
Object[] params = new Object[paramlen];
head -= paramlen;
System.arraycopy(stack, head+1, params, 0, paramlen);
stack[head] = ((Function)stack[head]).invoke(p, params);
break;
}
case Opcodes.CALV: {//calv <ubyte>
int paramlen = code[ct] & 0xff;
ct++;
Object[] params = new Object[paramlen];
head -= paramlen;
System.arraycopy(stack, head+1, params, 0, paramlen);
((Function)stack[head]).invoke(p, params);
head--;
break;
}
case Opcodes.CALLC: {// callc <ubyte> <ushort>
int paramlen = code[ct] & 0xff;
ct++;
Object[] params = new Object[paramlen];
head -= paramlen-1;
System.arraycopy(stack, head, params, 0, paramlen);
Function f = (Function) cpool[((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff)];
ct += 2;
stack[head] = f.invoke(p, params);
break;
}
case Opcodes.CALVC: {// calvc <ubyte> <ushort>
int paramlen = code[ct] & 0xff;
ct++;
Object[] params = new Object[paramlen];
head -= paramlen-1;
System.arraycopy(stack, head, params, 0, paramlen);
Function f = (Function) cpool[((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff)];
ct += 2;
stack[head] = f.invoke(p, params);
head--;
break;
}
//ARRAY INSTRUCTIONS
case Opcodes.NEWAA: {
stack[head] = new Object[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWBA: {
stack[head] = new byte[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWCA: {
stack[head] = new char[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWZA: {
stack[head] = new boolean[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWSA: {
stack[head] = new short[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWIA: {
stack[head] = new int[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWLA: {
stack[head] = new long[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWFA: {
stack[head] = new float[((Int32)stack[head]).value];
break;
}
case Opcodes.NEWDA: {
stack[head] = new double[((Int32)stack[head]).value];
break;
}
case Opcodes.AALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = ((Object[])stack[head])[at];
break;
}
case Opcodes.BALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = Int32.toInt32( ((byte[])stack[head])[at] );
break;
}
case Opcodes.CALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = Int32.toInt32( ((char[])stack[head])[at] );
break;
}
case Opcodes.ZALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = ((boolean[])stack[head])[at] ? Int32.ONE : Int32.ZERO;
break;
}
case Opcodes.SALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = Int32.toInt32( ((short[])stack[head])[at] );
break;
}
case Opcodes.IALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = Int32.toInt32( ((int[])stack[head])[at] );
break;
}
case Opcodes.LALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = new Int64( ((long[])stack[head])[at] );
break;
}
case Opcodes.FALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = new Float32( ((float[])stack[head])[at] );
break;
}
case Opcodes.DALOAD: {
int at = ((Int32)stack[head]).value;
head--;
stack[head] = new Float64( ((double[])stack[head])[at] );
break;
}
case Opcodes.AASTORE: {
Object val = stack[head];
int at = ((Int32)stack[head-1]).value;
Object[] array = (Object[])stack[head-2];
array[at] = val;
head -= 3;
break;
}
case Opcodes.BASTORE: {
int val = ((Int32)stack[head]).value;
int at = ((Int32)stack[head-1]).value;
byte[] array = (byte[])stack[head-2];
array[at] = (byte)val;
head -= 3;
break;
}
case Opcodes.CASTORE: {
char val = (char) ((Int32)stack[head]).value;
int at = ((Int32)stack[head-1]).value;
char[] array = (char[])stack[head-2];
array[at] = val;
head -= 3;
break;
}
case Opcodes.ZASTORE: {
boolean val = stack[head] != Int32.ZERO;
int at = ((Int32)stack[head-1]).value;
boolean[] array = (boolean[])stack[head-2];
array[at] = val;
head -= 3;
break;
}
case Opcodes.SASTORE: {
short val = (short)((Int32)stack[head]).value;
int at = ((Int32)stack[head-1]).value;
short[] array = (short[])stack[head-2];
array[at] = val;
head -= 3;
break;
}
case Opcodes.IASTORE: {
int val = ((Int32)stack[head]).value;
int at = ((Int32)stack[head-1]).value;
int[] array = (int[])stack[head-2];
array[at] = val;
head -= 3;
break;
}
case Opcodes.FASTORE: {
float val = ((Float32)stack[head]).value;
int at = ((Int32)stack[head-1]).value;
float[] array = (float[])stack[head-2];
array[at] = val;
head -= 3;
break;
}
case Opcodes.DASTORE: {
double val = ((Float64)stack[head]).value;
int at = ((Int32)stack[head-1]).value;
double[] array = (double[])stack[head-2];
array[at] = val;
head -= 3;
break;
}
case Opcodes.AALEN: {
stack[head] = Int32.toInt32(((Object[])stack[head]).length);
break;
}
case Opcodes.BALEN: {
stack[head] = Int32.toInt32(((byte[])stack[head]).length);
break;
}
case Opcodes.CALEN: {
stack[head] = Int32.toInt32(((char[])stack[head]).length);
break;
}
case Opcodes.ZALEN: {
stack[head] = Int32.toInt32(((boolean[])stack[head]).length);
break;
}
case Opcodes.SALEN: {
stack[head] = Int32.toInt32(((short[])stack[head]).length);
break;
}
case Opcodes.IALEN: {
stack[head] = Int32.toInt32(((int[])stack[head]).length);
break;
}
case Opcodes.LALEN: {
stack[head] = Int32.toInt32(((long[])stack[head]).length);
break;
}
case Opcodes.FALEN: {
stack[head] = Int32.toInt32(((float[])stack[head]).length);
break;
}
case Opcodes.DALEN: {
stack[head] = Int32.toInt32(((double[])stack[head]).length);
break;
}
case Opcodes.NEWMULTIARRAY: {
int dimension = code[ct] & 0xff;
ct++;
if (dimension < 2) throw new IllegalArgumentException();
int[] sizes = new int[dimension];
head -= dimension-1;
for (int i=0; i<dimension; i++) {
sizes[i] = ((Int32)stack[head+i]).value;
}
int type = code[ct];
ct++;
stack[head] = Arrays.newMultiArray(sizes, type);
break;
}
//SWITCH BRANCHING
case Opcodes.TABLESWITCH: {
int dflt = ((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff);
ct += 2;
int min = (code[ct] << 24)
| ((code[ct+1] & 0xff) << 16)
| ((code[ct+2] & 0xff) << 8)
| (code[ct+3] & 0xff);
ct += 4;
int max = (code[ct] << 24)
| ((code[ct+1] & 0xff) << 16)
| ((code[ct+2] & 0xff) << 8)
| (code[ct+3] & 0xff);
ct += 4;
int val = ((Int32)stack[head]).value;
head--;
if (val >= min && val <= max) {
ct += (val-min)*2;
ct = ((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff);
} else {
ct = dflt;
}
break;
}
case Opcodes.LOOKUPSWITCH: {
int dflt = ((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff);
ct += 2;
int count = ((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff);
ct += 2;
int val = ((Int32)stack[head]).value;
head--;
boolean matched = false;
for (int i=0; i<count && !matched; i++) {
int cand = (code[ct] << 24)
| ((code[ct+1] & 0xff) << 16)
| ((code[ct+2] & 0xff) << 8)
| (code[ct+3] & 0xff);
ct += 4;
if (val == cand) {
ct = ((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff);
matched = true;
} else {
ct += 2;
}
}
if (!matched) ct = dflt;
break;
}
//OTHERS
case Opcodes.CONCAT: { // concat <ubyte>
int n = code[ct] & 0xff;
ct++;
head -= n-1;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < n; i++) {
sb.append(Strings.toString(stack[head+i]));
}
stack[head] = sb.toString();
break;
}
case Opcodes.ACMP: {
head--;
boolean eq = (stack[head] == null) ? stack[head+1] == null : stack[head].equals(stack[head+1]);
stack[head] = eq ? Int32.ZERO : Int32.ONE;
break;
}
case Opcodes.RET_NULL:
return null;
case Opcodes.RETURN:
return stack[head];
case Opcodes.DUP: {
stack[head+1] = stack[head];
head++;
break;
}
case Opcodes.DUP2: {
stack[head+2] = stack[head];
stack[head+1] = stack[head-1];
head += 2;
break;
}
case Opcodes.SWAP: {
Object atmp = stack[head-1];
stack[head-1] = stack[head];
stack[head] = atmp;
break;
}
case Opcodes.LDC: { //ldc <ushort>
head++;
stack[head] = cpool[((code[ct] & 0xff) << 8) | (code[ct+1] & 0xff)];
ct += 2;
break;
}
case Opcodes.POP: {
head--;
break;
}
case Opcodes.BIPUSH: { //bipush <byte>
head++;
stack[head] = Int32.toInt32(code[ct]);
ct++;
break;
}
case Opcodes.SIPUSH: { //sipush <short>
head++;
stack[head] = Int32.toInt32((code[ct] << 8) | (code[ct+1]& 0xff));
ct += 2;
break;
}
case Opcodes.IINC: { //iinc <ubyte> <byte>
int idx = code[ct] & 0xff;
ct++;
int inc = code[ct];
ct++;
stack[idx] = Int32.toInt32(((Int32)stack[idx]).value + inc);
break;
}
case Opcodes.THROW: { //throw
throw new AlchemyException(((Int32)stack[head-1]).value, (String)stack[head]);
}
} /* the big switch */
} catch (ProcessKilledException pke) {
throw pke;
} catch (Throwable e) {
// the instruction on which error occured
ct--;
// filling exception with debug info
AlchemyException ae = (e instanceof AlchemyException) ? (AlchemyException)e : new AlchemyException(e);
if (dbgtable != null) {
int srcline = 0;
for (int i=1; i<dbgtable.length; i += 2) {
if (dbgtable[i+1] <= ct) srcline = dbgtable[i];
}
ae.addTraceElement(this, cpool[dbgtable[0]]+":"+srcline);
} else {
ae.addTraceElement(this, "+"+ct);
}
// catching or rethrowing
int jumpto = -1;
if (errtable != null) {
for (int i=0; i < errtable.length && jumpto < 0; i += 4)
if (ct >= errtable[i] && ct <= errtable[i+1]) {
jumpto = errtable[i+2];
head = localsize + errtable[i+3];
}
}
if (jumpto >= 0) {
stack[head] = ae;
ct = jumpto;
} else {
throw ae;
}
}
} /* the great while */
throw new ProcessKilledException();
}
}