KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > uka > ipd > coverage > recording > ByteCodeModifyer


1 /*
2  * Created on 28.08.2004
3  *
4  * written by Matthias Kempka
5  */

6 package de.uka.ipd.coverage.recording;
7
8 import org.apache.bcel.Constants;
9 import org.apache.bcel.classfile.Method;
10 import org.apache.bcel.generic.*;
11
12 import de.uka.ipd.coverage.junit.CoverageTestResult;
13
14 /**
15  * Created on 28.08.2004
16  * @author Matthias Kempka
17  */

18 public class ByteCodeModifyer {
19
20     /**
21      * weaves the callbacks at start and end of all BasicBlocks
22      * into all methods of the given ClassGen object.
23      * @param classGen Collecting parameter. This object is modified.
24      */

25     public static void weaveCallbacks(ClassGen classGen) {
26         Method[] methods = classGen.getMethods();
27         for (int i = 0; i < methods.length; i++) {
28             
29             if (!(methods[i].isAbstract())
30                     && !(methods[i].isNative())) {
31                 methods[i] = weaveCallbacksToMethod(classGen, methods[i]);
32             }
33         }
34         classGen.setMethods(methods);
35 // new ByteCodePrinter(classGen.getJavaClass()).printBytecode();
36
// debugActions(classGen);
37
}
38     
39
40     /**
41      * This is a big and ugly method that weaves the callbacks into
42      * the bytecode.
43      * @param classGen
44      * @param method
45      */

46     private static Method weaveCallbacksToMethod(ClassGen classGen, Method method) {
47         InstructionFactory factory = new InstructionFactory(classGen);
48         MethodGen methodGen = new MethodGen(
49                 method,
50                 classGen.getClassName(),
51                 classGen.getConstantPool());
52         InstructionList il =
53             BasicBlockIdentifyer.insertBlockDelimiters(methodGen);
54
55 // new ByteCodePrinter(classGen.getJavaClass()).printBytecode(System.out, methodGen.getMethod(), false);
56

57         InstructionHandle runnerHandle = il.getStart();
58         
59         String JavaDoc id = RegisteredMethod.createHashKey(
60                 classGen.getJavaClass(),
61                 method);
62         
63         // create and initialize the RegisteredMethod for this method
64
// This weaves in code like:
65
// RegisteredMethod rMethod = CentralRegistry.getRegisteredMethod(id);
66
LocalVariableGen lvar = methodGen.addLocalVariable(
67                 "rMethod", //$NON-NLS-1$
68
Type.getType(RegisteredMethod.class),
69                 il.getStart(),
70                 il.getEnd());
71         int index = lvar.getIndex(); // position of the new local variable
72
il.insert(runnerHandle, new PUSH(classGen.getConstantPool(), id));
73         il.insert(runnerHandle, factory.createInvoke(
74                 CoverageTestResult.class.getName(),
75                 "getRegisteredMethod", //$NON-NLS-1$
76
Type.getType(RegisteredMethod.class),
77                 new Type[] {Type.STRING},
78                 Constants.INVOKESTATIC));
79         il.insert(runnerHandle,
80                 InstructionFactory.createStore(Type.OBJECT, index));
81         
82         // search for BlockDelimiters and replace them with appropriate
83
// callbacks
84
InstructionHandle startHandle;
85         InstructionHandle endHandle;
86         
87         while (runnerHandle.getNext() != null) {
88             
89             // search for the beginning of the block
90
runnerHandle = skipToNextBlockDelimiter(runnerHandle);
91             startHandle = runnerHandle;
92             BlockDelimiter startDelimiter =
93                 (BlockDelimiter) startHandle.getInstruction();
94 // Logger.getInstance().debug("Block start found at PC "
95
// + startDelimiter.getPosition());
96

97             assert (startDelimiter.isStartBlock()) :
98                 "Expected Start delimiter, but what I found is no " + //$NON-NLS-1$
99
"start delimiter!"; //$NON-NLS-1$
100

101             // search for the end of the block
102
runnerHandle = runnerHandle.getNext();
103             runnerHandle = skipToNextBlockDelimiter(runnerHandle);
104             endHandle = runnerHandle;
105             BlockDelimiter endDelimiter =
106                 (BlockDelimiter) endHandle.getInstruction();
107 // Logger.getInstance().debug("Block end found at PC "
108
// + endDelimiter.getPosition());
109

110             assert (endDelimiter.isEndBlock()) :
111                 "Expected end delimiter, but what I found is no end delimiter!"; //$NON-NLS-1$
112

113             int start = startDelimiter.getPosition();
114             int end = endDelimiter.getPosition();
115             
116             // insert callbacks where the delimiters have been.
117
// BlockDelimiter at startHandle and endHandle are replaced to not
118
// disturb branch targets.
119

120             // block entry:
121
// This weaves in code like:
122
// rMethod.triggerEnterd(start,end);
123
startHandle.setInstruction(
124                     InstructionFactory.createLoad(Type.OBJECT, index));
125             il.append(startHandle, factory.createInvoke(
126                     RegisteredMethod.class.getName(),
127                     "triggerEntered", //$NON-NLS-1$
128
Type.VOID,
129                     new Type[] {Type.INT, Type.INT},
130                     Constants.INVOKEVIRTUAL));
131             il.append(startHandle, new PUSH(classGen.getConstantPool(), end));
132             il.append(startHandle, new PUSH(classGen.getConstantPool(), start));
133             
134             // block exit:
135
// This weaves in code like:
136
// rmethod.triggerExited(start, end);
137
endHandle.setInstruction(
138                     InstructionFactory.createLoad(Type.OBJECT, index));
139             il.append(endHandle, factory.createInvoke(
140                     RegisteredMethod.class.getName(),
141                     "triggerExited", //$NON-NLS-1$
142
Type.VOID,
143                     new Type[] {Type.INT, Type.INT},
144                     Constants.INVOKEVIRTUAL));
145             il.append(endHandle, new PUSH(classGen.getConstantPool(), end));
146             il.append(endHandle, new PUSH(classGen.getConstantPool(), start));
147             
148             // set runner to next position after the inserted instructions
149
runnerHandle = runnerHandle.getNext().getNext().getNext().getNext();
150             
151             // weave a callback for method exit
152
if (isMethodExit(runnerHandle)) {
153                 il.insert(runnerHandle,
154                         InstructionFactory.createLoad(Type.OBJECT, index));
155                 il.insert(runnerHandle, factory.createInvoke(
156                         RegisteredMethod.class.getName(),
157                         "leavingMethod", //$NON-NLS-1$
158
Type.VOID,
159                         new Type[] {},
160                         Constants.INVOKEVIRTUAL));
161             }
162                  
163         }
164         il.setPositions(true);
165         methodGen.setMaxLocals();
166         methodGen.setMaxStack();
167         return methodGen.getMethod();
168     }
169
170     /**
171      * @param runnerHandle
172      */

173     private static boolean isMethodExit(InstructionHandle runnerHandle) {
174         if (runnerHandle.getInstruction() instanceof ReturnInstruction
175                 || runnerHandle.getInstruction() instanceof ATHROW) {
176             return true;
177         }
178         return false;
179     }
180
181     /**
182      * returns an InstructionHandle to the next BlockDelimiter instruction
183      * by calling getNext() including the instruction the given handle points to.
184      */

185     private static InstructionHandle skipToNextBlockDelimiter(InstructionHandle runnerHandle) {
186         while (!(runnerHandle.getInstruction()
187                 instanceof BlockDelimiter)) {
188             runnerHandle = runnerHandle.getNext();
189         }
190         return runnerHandle;
191     }
192 }
193
Popular Tags