001 package edu.rice.cs.cunit.instrumentors.util; 002 003 import edu.rice.cs.cunit.classFile.ClassFile; 004 import edu.rice.cs.cunit.classFile.MethodInfo; 005 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo; 006 import edu.rice.cs.cunit.classFile.code.InstructionList; 007 import edu.rice.cs.cunit.util.IPredicate; 008 import edu.rice.cs.cunit.instrumentors.IInstrumentationStrategy; 009 010 /** 011 * Abstract instrumentation strategy that insert code before an opcode. 012 * 013 * @author Mathias Ricken 014 */ 015 public abstract class AInsertAtOpcodeStrategy implements IInstrumentationStrategy { 016 /** 017 * Predicate that decides if this class should be instrumented. 018 */ 019 protected IPredicate<ClassFile> _classPredicate; 020 021 /** 022 * Predicate that decides if this method should be instrumented. 023 */ 024 protected IPredicate.Binary<ClassFile, MethodInfo> _methodPredicate; 025 026 /** 027 * Predicate that decides if code should be inserted BEFORE this opcode. 028 */ 029 protected IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> _beforeOpcodePredicate; 030 031 /** 032 * Predicate that decides if code should be inserted AFTER this opcode. 033 */ 034 protected IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> _afterOpcodePredicate; 035 036 /** 037 * A predicate that always returns false, i.e. never causes code to be inserted. 038 */ 039 protected static final IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> OPCODE_NEVER = 040 new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() { 041 public Boolean apply(ClassFile classFile, MethodInfo methodInfo, InstructionList instructionList) { 042 return false; // never instrument 043 } 044 }; 045 046 /** 047 * Create a new strategy with the specified predicates. 048 * @param classPredicate predicate that decides if this class should be instrumented 049 * @param methodPredicate predicate that decides if this method should be instrumented 050 * @param beforeOpcodePredicate predicate that decides if this opcode should be instrumented 051 * @param afterOpcodePredicate predicate that decides if this opcode should be instrumented 052 */ 053 protected AInsertAtOpcodeStrategy(IPredicate<ClassFile> classPredicate, 054 IPredicate.Binary<ClassFile, MethodInfo> methodPredicate, 055 IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> beforeOpcodePredicate, 056 IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> afterOpcodePredicate) { 057 _classPredicate = classPredicate; 058 _methodPredicate = methodPredicate; 059 _beforeOpcodePredicate = beforeOpcodePredicate; 060 _afterOpcodePredicate = afterOpcodePredicate; 061 } 062 063 /** 064 * Instrument the class. 065 * 066 * @param cf class file info 067 */ 068 public void instrument(final ClassFile cf) { 069 // check if this is a target class 070 if (((cf.getClassAccessFlags() & ClassFile.ACC_INTERFACE) == 0) && // not an interface 071 (_classPredicate.apply(cf))) { // and class predicate is true 072 for(MethodInfo mi: cf.getMethods()) { 073 // check if this is a target method 074 if (((mi.getAccessFlags() & ClassFile.ACC_ABSTRACT) == 0) && // not abstract 075 ((mi.getAccessFlags() & ClassFile.ACC_NATIVE) == 0) && // not native 076 (_methodPredicate.apply(cf, mi))) { // and method predicate is true 077 CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo(); 078 InstructionList il = new InstructionList(codeAttr.getCode()); 079 080 boolean insertBefore = false; 081 boolean insertAfter = false; 082 do { 083 // check if code should be inserted before the opcode 084 if (_beforeOpcodePredicate.apply(cf, mi, il)) { 085 insertBefore = true; 086 insertInstructionsBefore(cf, mi, il); 087 } 088 // check if code should be inserted after the opcode 089 if (_afterOpcodePredicate.apply(cf, mi, il)) { 090 insertAfter = true; 091 insertInstructionsAfter(cf, mi, il); 092 } 093 } while(il.advanceIndex()); 094 095 boolean insertEndOfMethod = insertEndOfMethod(cf, mi, il, insertBefore, insertAfter); 096 097 if (insertBefore || insertAfter || insertEndOfMethod) { 098 codeAttr.setCode(il.getCode()); 099 modifyStackAndLocals(cf, mi, il, insertBefore, insertAfter, insertEndOfMethod); 100 } 101 } 102 } 103 } 104 } 105 106 /** 107 * Insert instructions before. 108 * NOTE: The method is expected to move back exactly to the instruction it was on. 109 * If it moves to a place before the instruction, an infinite loop may occur. 110 * If it moves to a place after the instruction, future inserts may not occur. 111 * @param cf class file 112 * @param mi method information 113 * @param il instruction list 114 */ 115 public abstract void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il); 116 117 /** 118 * Insert instructions after and advance past the instruction. 119 * NOTE: The method is expected to move exactly to the last instruction that was inserted. 120 * If it moves to a place before the instruction, an infinite loop may occur. 121 * If it moves to a place after the instruction, future inserts may not occur. 122 * @param cf class file 123 * @param mi method information 124 * @param il instruction list 125 */ 126 public abstract void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il); 127 128 /** 129 * Insert instructions at the end of the method. 130 * NOTE: The method is expected to move exactly to the last instruction that was inserted. 131 * If it moves to a place before the instruction, an infinite loop may occur. 132 * If it moves to a place after the instruction, future inserts may not occur. 133 * @param cf class file 134 * @param mi method information 135 * @param il instruction list 136 * @param insertBefore true if code was inserted before an opcode 137 * @param insertAfter true if code was inserted after an opcode 138 * @return true if code was inserted 139 */ 140 public abstract boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il, 141 boolean insertBefore, boolean insertAfter); 142 143 /** 144 * Modify the stack size and number of local variables so that the added instructions can execute. 145 * Called only once per method. 146 * @param cf class file 147 * @param mi method information 148 * @param il instruction list 149 * @param insertBefore true if code was inserted before an opcode 150 * @param insertAfter true if code was inserted after an opcode 151 * @param insertEndOfMethod true if code was inserted at the end of the method 152 */ 153 public abstract void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, 154 boolean insertBefore, boolean insertAfter, boolean insertEndOfMethod); 155 156 /** 157 * Instrumentation of all classes is done. 158 */ 159 public void done() { 160 // nothing to do 161 } 162 }