package alt.jiapi.reflect;
import java.util.List;
import java.util.LinkedList;
import alt.jiapi.file.ConstantPool;
import alt.jiapi.reflect.instruction.Opcodes;
import alt.jiapi.reflect.instruction.Invocation;
import alt.jiapi.reflect.instruction.FieldAccess;
import alt.jiapi.reflect.instruction.CPInstruction;
/**
* Class InstructionParser.
*
* @author Mika Riekkinen
*/
class InstructionParser {
/**
* Parse bytecode and create a List of Instructions out
* of it.
*
* @param byteCode Bytecode to be parsed.
* @return a List of Instructions.
* @exception
*/
List createInstructionList(byte[] byteCode, ConstantPool cp) {
List list = new LinkedList();
boolean branchesFound = false;
/*
* First loop through bytecode and create raw instructions.
* After loop, resolve branch/switch targets.
*/
for (int i = 0; i < byteCode.length; i++) {
byte opcode = byteCode[i];
Instruction ins = null;
int iMod = 0;
//System.out.println("Instruction " + Opcodes.opcodeStrings[opcode&0xff]);
switch(opcode) {
case Opcodes.NOP:
case Opcodes.ACONST_NULL:
case Opcodes.ICONST_M1:
case Opcodes.ICONST_0:
case Opcodes.ICONST_1:
case Opcodes.ICONST_2:
case Opcodes.ICONST_3:
case Opcodes.ICONST_4:
case Opcodes.ICONST_5:
case Opcodes.LCONST_0:
case Opcodes.LCONST_1:
case Opcodes.FCONST_0:
case Opcodes.FCONST_1:
case Opcodes.FCONST_2:
case Opcodes.DCONST_0:
case Opcodes.DCONST_1:
ins = new Instruction(new byte[] { byteCode[i] });
break;
case Opcodes.BIPUSH:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1] });
iMod = 1;//i++;
break;
case Opcodes.SIPUSH:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] });
iMod = 2;//i += 2;
break;
case Opcodes.LDC:
ins = new CPInstruction(new byte[] { byteCode[i],
byteCode[i+1] }, cp);
iMod = 1;//i++;
break;
case Opcodes.LDC_W:
case Opcodes.LDC2_W:
ins = new CPInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] }, cp);
iMod = 2;//i += 2;
break;
case Opcodes.ILOAD:
case Opcodes.LLOAD:
case Opcodes.FLOAD:
case Opcodes.DLOAD:
case Opcodes.ALOAD:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1] });
iMod = 1;//i++;
break;
case Opcodes.ILOAD_0:
case Opcodes.ILOAD_1:
case Opcodes.ILOAD_2:
case Opcodes.ILOAD_3:
case Opcodes.LLOAD_0:
case Opcodes.LLOAD_1:
case Opcodes.LLOAD_2:
case Opcodes.LLOAD_3:
case Opcodes.FLOAD_0:
case Opcodes.FLOAD_1:
case Opcodes.FLOAD_2:
case Opcodes.FLOAD_3:
case Opcodes.DLOAD_0:
case Opcodes.DLOAD_1:
case Opcodes.DLOAD_2:
case Opcodes.DLOAD_3:
case Opcodes.ALOAD_0:
case Opcodes.ALOAD_1:
case Opcodes.ALOAD_2:
case Opcodes.ALOAD_3:
ins = new Instruction(new byte[] { byteCode[i] });
break;
case Opcodes.IALOAD:
case Opcodes.LALOAD:
case Opcodes.FALOAD:
case Opcodes.DALOAD:
case Opcodes.AALOAD:
case Opcodes.BALOAD:
case Opcodes.CALOAD:
case Opcodes.SALOAD:
ins = new Instruction(new byte[] { byteCode[i] });
break;
case Opcodes.ISTORE:
case Opcodes.LSTORE:
case Opcodes.FSTORE:
case Opcodes.DSTORE:
case Opcodes.ASTORE:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1] });
iMod = 1;//i++;
break;
case Opcodes.ISTORE_0:
case Opcodes.ISTORE_1:
case Opcodes.ISTORE_2:
case Opcodes.ISTORE_3:
case Opcodes.LSTORE_0:
case Opcodes.LSTORE_1:
case Opcodes.LSTORE_2:
case Opcodes.LSTORE_3:
case Opcodes.FSTORE_0:
case Opcodes.FSTORE_1:
case Opcodes.FSTORE_2:
case Opcodes.FSTORE_3:
case Opcodes.DSTORE_0:
case Opcodes.DSTORE_1:
case Opcodes.DSTORE_2:
case Opcodes.DSTORE_3:
case Opcodes.ASTORE_0:
case Opcodes.ASTORE_1:
case Opcodes.ASTORE_2:
case Opcodes.ASTORE_3:
case Opcodes.IASTORE:
case Opcodes.LASTORE:
case Opcodes.FASTORE:
case Opcodes.DASTORE:
case Opcodes.AASTORE:
case Opcodes.BASTORE:
case Opcodes.CASTORE:
case Opcodes.SASTORE:
case Opcodes.POP:
case Opcodes.POP2:
case Opcodes.DUP:
case Opcodes.DUP_X1:
case Opcodes.DUP_X2:
case Opcodes.DUP2:
case Opcodes.DUP2_X1:
case Opcodes.DUP2_X2:
case Opcodes.SWAP:
case Opcodes.IADD:
case Opcodes.LADD:
case Opcodes.FADD:
case Opcodes.DADD:
case Opcodes.ISUB:
case Opcodes.LSUB:
case Opcodes.FSUB:
case Opcodes.DSUB:
case Opcodes.IMUL:
case Opcodes.LMUL:
case Opcodes.FMUL:
case Opcodes.DMUL:
case Opcodes.IDIV:
case Opcodes.LDIV:
case Opcodes.FDIV:
case Opcodes.DDIV:
case Opcodes.IREM:
case Opcodes.LREM:
case Opcodes.FREM:
case Opcodes.DREM:
case Opcodes.INEG:
case Opcodes.LNEG:
case Opcodes.FNEG:
case Opcodes.DNEG:
case Opcodes.ISHL:
case Opcodes.LSHL:
case Opcodes.ISHR:
case Opcodes.LSHR:
case Opcodes.IUSHR:
case Opcodes.LUSHR:
case Opcodes.IAND:
case Opcodes.LAND:
case Opcodes.IOR:
case Opcodes.LOR:
case Opcodes.IXOR:
case Opcodes.LXOR:
ins = new Instruction(new byte[] { byteCode[i] });
break;
case Opcodes.IINC:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] });
iMod = 2;//i += 2;
break;
case Opcodes.I2L:
case Opcodes.I2F:
case Opcodes.I2D:
case Opcodes.L2I:
case Opcodes.L2F:
case Opcodes.L2D:
case Opcodes.F2I:
case Opcodes.F2L:
case Opcodes.F2D:
case Opcodes.D2I:
case Opcodes.D2L:
case Opcodes.D2F:
case Opcodes.I2B:
case Opcodes.I2C:
case Opcodes.I2S:
case Opcodes.LCMP:
case Opcodes.FCMPL:
case Opcodes.FCMPG:
case Opcodes.DCMPL:
case Opcodes.DCMPG:
ins = new Instruction(new byte[] { byteCode[i] });
break;
case Opcodes.IFEQ:
case Opcodes.IFNE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFLE:
case Opcodes.IF_ICMPEQ:
case Opcodes.IF_ICMPNE:
case Opcodes.IF_ICMPLT:
case Opcodes.IF_ICMPGE:
case Opcodes.IF_ICMPGT:
case Opcodes.IF_ICMPLE:
case Opcodes.IF_ACMPEQ:
case Opcodes.IF_ACMPNE:
case Opcodes.GOTO:
case Opcodes.JSR:
branchesFound = true;
ins = new BranchInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2]});
iMod = 2;//i += 2;
break;
case Opcodes.RET:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1]});
iMod = 1;//i += 1;
break;
case Opcodes.TABLESWITCH:
branchesFound = true;
int tspCount = 3 - (i % 4);
byte l1 = byteCode[i + tspCount + 5];
byte l2 = byteCode[i + tspCount + 6];
byte l3 = byteCode[i + tspCount + 7];
byte l4 = byteCode[i + tspCount + 8];
int low = (((int)l1) << 24) | (((int)l2) << 16) |
(((int)l3) << 8) | l4;
byte h1 = byteCode[i + tspCount + 9];
byte h2 = byteCode[i + tspCount + 10];
byte h3 = byteCode[i + tspCount + 11];
byte h4 = byteCode[i + tspCount + 12];
int high = (((int)h1) << 24) | (((int)h2) << 16) |
(((int)h3) << 8) | h4;
int size = high - low + 1;
int bSize = size * 4; // Size of offset table in bytes
byte[] tsBytes = new byte[13 + tspCount + bSize];
for (int k = 0; k < tsBytes.length; k++) {
tsBytes[k] = byteCode[i + k];
}
iMod = tsBytes.length -1;//i += tsBytes.length -1;
ins = new SwitchInstruction(tsBytes, tspCount, size);
break;
case Opcodes.LOOKUPSWITCH:
branchesFound = true;
int pCount = 3 - (i % 4);
// count padding
// NOTE: Following is a bug
byte np1 = byteCode[i + pCount + 5];
byte np2 = byteCode[i + pCount + 6];
byte np3 = byteCode[i + pCount + 7];
byte np4 = byteCode[i + pCount + 8];
int nPairs = (np1 << 24) & 0xff000000 | (np2 << 16) & 0xff0000|
(np3 << 8) & 0xff00 | (np4 & 0xff);
int lbSize = nPairs * 4 * 2; // Size of offset table in bytes
byte[] lsBytes = new byte[9 + pCount + lbSize ];
for (int k = 0; k < lsBytes.length; k++) {
lsBytes[k] = byteCode[i + k];
}
iMod = lsBytes.length -1;//i += lsBytes.length -1;
ins = new SwitchInstruction(lsBytes, pCount, nPairs);
break;
case Opcodes.IRETURN:
case Opcodes.LRETURN:
case Opcodes.FRETURN:
case Opcodes.DRETURN:
case Opcodes.ARETURN:
case Opcodes.RETURN:
ins = new Instruction(new byte[] { byteCode[i] });
break;
case Opcodes.GETSTATIC:
case Opcodes.PUTSTATIC:
case Opcodes.GETFIELD:
case Opcodes.PUTFIELD:
ins = new FieldAccess(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2]}, cp);
iMod = 2;//i += 2;
break;
case Opcodes.INVOKEVIRTUAL:
case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC:
// BUG: what is the stack consumption
// cp.getMethodDescriptor(....);
ins = new Invocation(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] }, cp);
iMod = 2;//i += 2;
break;
case Opcodes.INVOKEINTERFACE:
byte count = byteCode[i+3];
ins = new Invocation(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2],
count,
byteCode[i+4]}, cp);
iMod = 4;//i += 4;
break;
case Opcodes.XXXUNUSEDXXX:
new Instruction(new byte[] { byteCode[i]});
break;
case Opcodes.NEW:
ins = new CPInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] }, cp);
iMod = 2;//i += 2;
break;
case Opcodes.NEWARRAY:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1] });
iMod = 1; //i += 1;
break;
case Opcodes.ANEWARRAY:
ins = new CPInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] }, cp);
iMod = 2;//i += 2;
break;
case Opcodes.ARRAYLENGTH:
ins = new Instruction(new byte[] { byteCode[i] } );
break;
case Opcodes.ATHROW:
ins = new Instruction(new byte[] { byteCode[i] } );
break;
case Opcodes.CHECKCAST:
ins = new CPInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] }, cp);
iMod = 2;//i += 2;
break;
case Opcodes.INSTANCEOF:
ins = new CPInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] }, cp);
iMod = 2;//i += 2;
break;
case Opcodes.MONITORENTER:
case Opcodes.MONITOREXIT:
ins = new Instruction(new byte[] { byteCode[i] });
break;
case Opcodes.WIDE:
if (byteCode[i + 1] == Opcodes.IINC) {
// Format 2:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2],
byteCode[i+3],
byteCode[i+4],
byteCode[i+5] });
iMod = 5;//i += 5;
}
else {
// Format 1:
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2],
byteCode[i+3] });
iMod = 3;//i += 3;
}
break;
case Opcodes.MULTIANEWARRAY:
byte dim = byteCode[i+4];
ins = new Instruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2],
byteCode[i+3],
dim }/*, dim*/);
iMod = 4;//i += 4;
break;
case Opcodes.IFNULL:
case Opcodes.IFNONNULL:
branchesFound = true;
ins = new BranchInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2] });
iMod = 2;//i += 2;
break;
case Opcodes.GOTO_W:
branchesFound = true;
ins = new BranchInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2],
byteCode[i+3],
byteCode[i+4] });
iMod = 4;//i += 4;
break;
case Opcodes.JSR_W:
branchesFound = true;
ins = new BranchInstruction(new byte[] { byteCode[i],
byteCode[i+1],
byteCode[i+2],
byteCode[i+3],
byteCode[i+4] });
iMod = 4;//i += 4;
break;
case Opcodes.BREAKPOINT:
case Opcodes.IMPDEP1:
case Opcodes.IMPDEP2:
ins = new Instruction(new byte[] { byteCode[i] });
break;
}
ins.setOffset((short)i);
i += iMod;
list.add(ins);
}
// Now we have created raw Instructions.
// If we have created any instruction in branch family,
// resolve their target instructions.
if (branchesFound) {
// find branch targets
for (int i = 0; i < list.size(); i++) {
Instruction ins = (Instruction)list.get(i);
if (ins instanceof BranchInstruction) {
// Simple branch
BranchInstruction bi = (BranchInstruction)ins;
int offset = bi.getTargetOffset();
Instruction target = null;
//target = findTarget(list, i, offset);
//target = findTarget(list, 0, (bi.getOffset() + offset));
target = findTarget(list, (bi.getOffset() + offset));
if (target != null) {
bi.setTarget(target);
}
else {
System.out.println("Error locating branch target for instruction at " + i + ": " + bi);
}
}
else if (ins instanceof SwitchInstruction) {
// complex branch
SwitchInstruction si = (SwitchInstruction)ins;
int dOffset = si.getDefaultOffset();
// default target:
Instruction target = null;
target = findTarget(list, i, dOffset);
si.setDefault(target);
// targets:
int[] tOffsets = si.getTargetOffsets();
Instruction[] targets = new Instruction[tOffsets.length];
for (int j = 0; j < targets.length; j++) {
targets[j] = findTarget(list, i, tOffsets[j]);
}
si.setTargets(targets);
}
}
}
return list;
}
/**
* Finds a jump target
*/
private Instruction findTarget(List list, int offset) {
int idx = 0;
int currentOffset = 0;
while (currentOffset != offset) {
if (idx == list.size()) {
return null;
}
Instruction ins = (Instruction)list.get(idx);
currentOffset += ins.length();
idx++;
}
return (Instruction)list.get(idx);
}
/**
* Finds a jump target
*/
private Instruction findTarget(List list, int idx, int offset) {
int offs = offset;
int dir = 1;
if (offset < 0) {
offs = -offset;
dir = -1;
}
int i = 0;
while(i < offs) {
Instruction ins = (Instruction)list.get(idx);
i += ins.length();
idx += dir;
}
// Replace 'target' instruction with TargetInstruction
Instruction target = (Instruction)list.get(idx);
TargetInstruction ti = new TargetInstruction(target);
list.set(idx, ti);
return ti;
}
}