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 }