package Sequenic.T2ext.Instrumenter; import java.io.Serializable; import java.util.Iterator; import java.util.LinkedList; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.ConstantPushInstruction; import org.apache.bcel.generic.FieldOrMethod; import org.apache.bcel.generic.IINC; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.LDC; import org.apache.bcel.generic.LDC2_W; import org.apache.bcel.generic.LocalVariableInstruction; import org.apache.bcel.generic.MULTIANEWARRAY; import org.apache.bcel.generic.NEWARRAY; import org.apache.bcel.generic.ObjectType; import org.apache.bcel.generic.Select; import org.apache.bcel.generic.TypedInstruction; final class InstructionsBlock implements Serializable { /** * */ private static final long serialVersionUID = 162406091701922L; private LinkedList instructions = new LinkedList(); private LinkedList strings = new LinkedList(); public void addInstruction(Instruction ins, ConstantPoolGen cpg) { final int opcode = ins.getOpcode(); //Store opcode for all instructions instructions.add(opcode); switch (opcode) { //Zero-operand instructions: nop and constants (null,-1,0,1,2,3,4,5,0L,1L,0.0f,1.0f,0.0,1.0) case 0: //NOP - do nothing case 1: //ACONST_NULL, should we do something with "Type type = ins.getType(ConstantPoolGen cp)"? case 2: //ICONST_M1 case 3: //ICONST_0 case 4: //ICONST_1 case 5: //ICONST_2 case 6: //ICONST_3 case 7: //ICONST_4 case 8: //ICONST_5 case 9: //LCONST_0 case 10: //LCONST_1 case 11: //FCONST_0 case 12: //FCONST_1 case 13: //FCONST_2 case 14: //DCONST_0 case 15: //DCONST_1 break; ////////// Push byte or short as int case 16: //BIPUSH, single 1-byte operand in range [-128, 127] case 17: //SIPUSH, single 2-byte operand in range [-32768, 32767] instructions.add(((ConstantPushInstruction) ins).getValue().intValue()); break; ////////// Load Constant from constant pool case 18: //LDC - int, float, string or Class case 19: //LDC_W - idem, but for 2-byte (wide) indices { Object cons = ((LDC) ins).getValue(cpg); if (cons instanceof String) addString((String) cons); else if(cons instanceof Integer) instructions.add((Integer) cons); else if(cons instanceof Float) instructions.add(Float.floatToIntBits((Float) cons)); //When using the newest, unreleased, BCEL: else if(cons instanceof ObjectType) { addString(((ObjectType) cons).getClassName()); //To distinguish the String Object "java/lang/Object" from the Class Object Object.class addString(".class"); } // BCEL 5.2 code // else if(cons instanceof ConstantClass) // { // addString( (String) ((ConstantClass) cons).getConstantValue(cpg.getConstantPool())); // //To distinguish the String Object "java/lang/Object" from the Class Object Object.class // addString(".class"); // } else throw new Error("LDC should not refer to opbject of type "+cons.getClass().getCanonicalName() + " toString: "+cons.toString()); } break; case 20: //LDC2_W - double or long { Number cons = ((LDC2_W) ins).getValue(cpg); long bits; if (cons instanceof Double) bits = Double.doubleToLongBits(cons.doubleValue()); else if(cons instanceof Long) bits = cons.longValue(); else throw new Error("LDC2_W should not refer to opbject of type "+cons.getClass().getCanonicalName() + " toString: "+cons.toString()); instructions.add((int) (bits >>> 32)); instructions.add((int) bits); } break; ///////// Increase integer at local variable, two operands: increment value and index case 132: //IINC instructions.add(((IINC) ins).getIncrement()); //increment is in range [-32768, 32767] //fall-through to add local variable index //////// Load a local variable, single operand instruction case 21: //ILOAD - load integer from local variable at index N, where N is a given operand case 22: //LLOAD - idem but long case 23: //FLOAD - float case 24: //DLOAD - double case 25: //ALOAD - object //////// Store a local variable, single operand instruction case 54: //ISTORE - store integer to local variable at index N, where N is a given operand case 55: //LSTORE - idem but long case 56: //FSTORE - float case 57: //DLOAD - double case 58: //ALOAD - object //add local variable index, is in range [0,0xFFFF] instructions.add(((LocalVariableInstruction) ins).getIndex()); break; //Load local variable with index 0,1,2 or 3, zero operand instruction case 26: // ILOAD_0 - load integer from local variable at index 0 case 27: // ILOAD_1 case 28: // ILOAD_2 case 29: // ILOAD_3 case 30: // LLOAD_0 - long case 31: // LLOAD_1 case 32: // LLOAD_2 case 33: // LLOAD_3 case 34: // FLOAD_0 - float case 35: // FLOAD_1 case 36: // FLOAD_2 case 37: // FLOAD_3 case 38: // DLOAD_0 - double case 39: // DLOAD_1 case 40: // DLOAD_2 case 41: // DLOAD_3 case 42: // ALOAD_0 - object case 43: // ALOAD_1 case 44: // ALOAD_2 case 45: // ALOAD_3 //Load from array, zero-operand instructions case 46: //IALOAD case 47: //LALOAD case 48: //FALOAD case 49: //DALOAD case 50: //AALOAD case 51: //BALOAD case 52: //CALOAD case 53: //SALOAD //54-58 are after 25, they share the same code as 21-25 //Store local variable with index 0,1,2 or 3, zero operand instruction case 59: // ISTORE_0 - store integer to local variable at index 0 case 60: // 1 case 61: // 2 case 62: // 3 case 63: // LSTORE_0 - long case 64: // 1 case 65: // 2 case 66: // 3 case 67: // FSTORE_0 - float case 68: // 1 case 69: // 2 case 70: // 3 case 71: // DSTORE_0 - double case 72: // 1 case 73: // 2 case 74: // 3 case 75: // ASTORE_0 - object case 76: // 1 case 77: // 2 case 78: // 3 //Store to array, zero-operand case 79: //IASTORE case 80: //LASTORE case 81: //FASTORE case 82: //DASTORE case 83: //AASTORE case 84: //BASTORE case 85: //CASTORE case 86: //SASTORE //Zero-operand case 87: //POP - Stack manipulation case 88: //POP2 case 89: //DUP case 90: //DUP_X1 case 91: //DUP_X2 case 92: //DUP2 case 93: //DUP2_X1 case 94: //DUP2_X2 case 95: //SWAP case 96: //IADD - arithmetic: + * / % case 97: //LADD case 98: //FADD case 99: //DADD case 100: //ISUB case 101: //LSUB case 102: //FSUB case 103: //DSUB case 104: //IMUL case 105: //LMUL case 106: //FMUL case 107: //DMUL case 108: //IDIV case 109: //LDIV case 110: //FDIV case 111: //DDIV case 112: //IREM case 113: //LREM case 114: //FREM case 115: //DREM case 116: //INEG - negation case 117: //LNEG case 118: //FNEG case 119: //DNEG case 120: //ISHL - bit shifting case 121: //LSHL case 122: //ISHR case 123: //LSHR case 124: //IUSHR case 125: //LUSHR case 126: //IAND - logical operators case 127: //LAND case 128: //IOR case 129: //LOR case 130: //IXOR case 131: //LXOR //132 is IINC, precedes case 21 case 133: //I2L - primitive conversion case 134: //I2F case 135: //I2D case 136: //L2I case 137: //L2F case 138: //L2D case 139: //F2I case 140: //F2L case 141: //F2D case 142: //D2I case 143: //D2L case 144: //D2F case 145: //I2B case 146: //I2C case 147: //I2S case 148: //LCMP -comparison case 149: //FCMPL case 150: //FCMPG case 151: //DCMPL case 152: //DCMPG //Branching, single-operand, but operand is branch offset: we don't store it //So treat as zero-operand case 153: //IFEQ x==0 --Int compare case 154: //IFNE x!=0 case 155: //IFLT x <0 case 156: //IFGE x>=0 case 157: //IFGT x >0 case 158: //IFLE x<=0 case 159: //IF_ICMPEQ x==y case 160: //IF_ICMPNE x!=y case 161: //IF_ICMPLT x =y case 163: //IF_ICMPGT x >y case 164: //IF_ICMPLE x<=y case 165: //IF_ACMPEQ A==B --Object compare case 166: //IF_ACMPNE A!=B case 167: //GOTO - unconditional jump case 168: //JSR - unconditional jump to subroutine, used to implement finally. // It uses RET to proceed to next instruction after JSR case 169: //RET - return from subroutine, go to instruction following the last invoked JSR case 198: //IFNULL A==null case 199: //IFNONNULL A!=null case 200: //GOTO_W case 201: //JSR_W break; ////////// SWITCH - there are two possible implementations for switch constructs such as this code... ;-) case 170: //TABLESWITCH case 171: //LOOKUPSWITCH int[] matchs = ((Select) ins).getMatchs(); instructions.add(matchs.length); for(int i : matchs) instructions.add(i); break; //zero-operand return statements, terminate method case 172: //IRETURN - return int case 173: //LRETURN - long case 174: //FRETURN - float case 175: //DRETURN - double case 176: //ARETURN - object case 177: //RETURN - void break; ////////// Field and Method instructions case 178: //GETSTATIC static field case 179: //PUTSTATIC case 180: //GETFIELD instance field case 181: //PUTFIELD case 182: //INVOKEVIRTUAL instance method case 183: //INVOKESPECIAL non-virtual instance (initializer, super, final or private) case 184: //INVOKESTATIC static method case 185: //INVOKEINTERFACE interface method { FieldOrMethod fm = (FieldOrMethod) ins; addString(fm.getReferenceType(cpg).getSignature()); addString(fm.getName(cpg)); addString(fm.getSignature(cpg)); } break; //186 is xxxunusedxxx, moved to default case ////////// Type Instructions (except 188,190,191), single operand refering to a Class type in ConstantPool. case 197: //multianewarray - multidimensional array, e.g. int[2][3], NOT int[][], that's ANEWARRAY //contains second operand: unsigned byte with number of dimensions in range [1,255] instructions.add((int) ((MULTIANEWARRAY) ins).getDimensions()); //fall through to add Type signature. case 187: //NEW - new Object //188 case 189: //ANEWARRAY - new single-dimension Object array //190 //191 case 192: //CHECKCAST - (type) A case 193: //INSTANCEOF - A instanceof type /* BCEL has two similar interfaces: * LoadClass specifies a type that is forced to be resolved when the instruction is executed * TypedInstruction specifies the type of the instructions * Example: "new int[][]" uses ANEWARRAY (a new single-dimension Object array containing int[] objects). * This does not cause any Classes to be loaded/resolved. * So we are actually interested in TypedInstruction. */ addString(((TypedInstruction) ins).getType(cpg).getSignature()); break; /////////// Single 1-byte operand in range [4,11] signifying type, e.g. T_INT = 10 case 188: //NEWARRAY new single-dimension primitive array instructions.add((int) ((NEWARRAY) ins).getTypecode()); break; //Zero-operand case 190: //ARRAYLENGTH case 191: //ATHROW - throw exception //192,193, see above, type instructions case 194: //MONITORENTER case 195: //MONITOREXIT case 196: //WIDE - widen local variable reference from 8 bit [0-255] to 16 bit [0-65535] // will be followed by some local variable instruction, so ignore. break; //197 see above, type instructions //198-201: IF(NON)NULL, GOTO_W and JSR_W moved after other branching instructions 153-169 //reserved: 202 breakpoint 254, 255 impdep1+2, should not occur in Class file //Other: 203-254 either not specified, or _QUICK variants, should not occur in Class file //Unknown or invalid instruction: throw error. case 186: //INVOKEDYNAMIC in Java 7 (see ASM)? Reserved for historical reasons, unused in Java <6 default: throw new Error("Unknown instruction with opcode "+opcode+" and class "+ins.getClass().getCanonicalName()+" toString: "+ins.toString(true)); } } private void addString(String s) { strings.add(s.intern()); } /** Indicates whether two nodes are lexically equivalent. * * @param a * @param b * @return */ public static boolean lexEquivalent(InstructionsBlock a, InstructionsBlock b) { //Opcodes and operands Iterator i = a.instructions.iterator(); Iterator j = b.instructions.iterator(); while(i.hasNext() && j.hasNext()) { if(i.next().intValue()!=j.next().intValue()) return false; } if(i.hasNext() || j.hasNext()) return false; //String constants Iterator s = a.strings.iterator(); Iterator t = b.strings.iterator(); while(s.hasNext() && t.hasNext()) if(!s.next().equals(t.next())) return false; if(s.hasNext() || t.hasNext()) return false; return true; } @Override public String toString() { return instructions.toString() + strings.toString(); } }