KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > alt > jiapi > instrumentor > MethodCallInstrumentor


1 /*
2  * Copyright (C) 2001 Mika Riekkinen, Joni Suominen
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18
19 package alt.jiapi.instrumentor;
20
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Modifier JavaDoc;
23 import java.util.HashMap JavaDoc;
24
25 //import java.util.logging.Logger;
26
import org.apache.log4j.Category;
27
28 import alt.jiapi.Runtime;
29 import alt.jiapi.reflect.InstructionFactory;
30 import alt.jiapi.reflect.InstructionList;
31 import alt.jiapi.reflect.JiapiClass;
32 import alt.jiapi.reflect.JiapiField;
33 import alt.jiapi.reflect.JiapiMethod;
34 import alt.jiapi.reflect.JiapiRuntimeException;
35 import alt.jiapi.reflect.Loader;
36 import alt.jiapi.reflect.SignatureUtil;
37
38 /**
39  * This instrumentor creates method calls.
40  * It will create calls to either static or virtual calls.<p>
41  * If a virtual call is made, a class is added an extra field
42  * to hold a reference to Object specified by hook-method.<p>
43  *
44  *
45  * NOTE : MethodCallInstrumentor is still under construction on some parts
46  * of it.
47  * At the moment, it is assumed that hookmethod has exactly two
48  * arguments, first of which will be 'this' if available, or String
49  * if not available,
50  * and second the name of the method being processed.
51  * This is likely to change to a more dynamic thing.
52  *
53  * @author Mika Riekkinen
54  * @author Joni Suominen
55  * @version $Revision: 1.26 $ $Date: 2004/03/21 12:50:08 $
56  */

57 public class MethodCallInstrumentor extends AbstractInstrumentor {
58     private static Category log = Category.getInstance(MethodCallInstrumentor.class);
59
60     protected Hook hook;
61     protected Class JavaDoc hookClass;
62     protected Method JavaDoc hookMethod;
63     protected boolean isDynamic;
64     
65     private String JavaDoc jiapiFieldName;
66
67     // Dynamic initializer
68
{
69         // Later on, we could ask Runtime to provide other
70
// field names.
71
jiapiFieldName = "__jiapi_field";
72     }
73
74     /**
75      * Chain which is used to pre-instrument a field to the target
76      * class when making dynamic calls.
77      */

78     protected InstrumentorChain preChain;
79
80     /**
81      * Chain which is used to post-instrument an initialization
82      * for the field which will hold a reference to an target instance
83      * which will be called when making dynamic calls.
84      */

85     protected InstrumentorChain postChain;
86
87     /**
88      * Empty constructor needed when the patch is dynamically instantiated.
89      * The hook has to be set afterwards with setHook method.
90      * @see #setHook
91      */

92     public MethodCallInstrumentor() {
93     }
94
95     /**
96      * Constructor. Hook defines a method, that will be called
97      * by the bytecode instrumented.
98      *
99      * @param hook A Hook to be called by this Instrumentor.
100      * @see Hook
101      */

102     public MethodCallInstrumentor(Hook hook) {
103         setHook(hook);
104     }
105
106     /**
107      * Constructor. This constructor creates MethodCallInstrumentor
108      * with the help of java.lang.reflect.Method.
109      *
110      * @param hookClass A class of the hook, that is to be called
111      * @param hookMethod A method, that is called. The method must
112      * a member of the class.
113      * @see java.lang.reflect.Method
114      */

115     public MethodCallInstrumentor(Class JavaDoc hookClass,
116                                   java.lang.reflect.Method JavaDoc hookMethod) {
117         // A bug in here. We don't have Hooks' instance.
118
// postInstrument will fail. 'Class hookClass' should be changed to
119
// 'Object instance'
120
this.hookClass = hookClass;
121         this.hookMethod = hookMethod;
122         this.isDynamic = !Modifier.isStatic(hookMethod.getModifiers());
123     }
124
125
126     /**
127      * Instruments given instruction list so, that it will make a
128      * call to specified by Hook given to this class. Instructions will be
129      * added to the end of the instruction list.<p>
130      * If a method being instrumented is native or abstract, instrumentation
131      * is skipped and instruction list is just forwarded to next in chain.
132      *
133      * @param il InstructionList to add new instructions to.
134      */

135     public void instrument(InstructionList il) {
136         log.info("Instrumenting " + getCurrentClass().getName() + "." +
137                  il.getDeclaringMethod().getName());
138
139         JiapiMethod jm = il.getDeclaringMethod();
140         // Native or abstract methods can't be instrumented.
141
int modifiers = jm.getModifiers();
142         if (Modifier.isNative(modifiers) || Modifier.isAbstract(modifiers)) {
143             log.info("skipping abstract or native method: " +
144                      getCurrentClass().getName() + "." + jm.getName());
145
146             forward(il);
147             return;
148         }
149
150         if (Modifier.isInterface(modifiers)) {
151             log.info("Will not instrument interface " +
152                      getCurrentClass().getName());
153             forward(il);
154             return;
155         }
156
157 // if ("<clinit>".equals(jm.getName())) {
158
// System.out.println("Will not instrument <clinit>");
159
// log.debug("Will not instrument <clinit>");
160
// forward(il);
161
// return;
162
// }
163

164
165         patchInstructionList(il, getInstrumentation());
166         
167         forward(il);
168     }
169
170
171     /**
172      * Instrument instruction list.
173      */

174     private void patchInstructionList(InstructionList il,
175                                       Instrumentation instrumentation) {
176         //System.out.println(">> " + il.getDeclaringMethod());
177
InstructionFactory factory = il.getInstructionFactory();
178         if (factory == null) {
179             throw new NullPointerException JavaDoc("Got null factory");
180         }
181
182         int currentMethodModifiers = il.getDeclaringMethod().getModifiers();
183
184         JiapiClass clazz = getCurrentClass();
185         Class JavaDoc[] hookParams = hookMethod.getParameterTypes();
186
187         if (isDynamic) {
188             log.debug("Hook is dynamic");
189             // Obtain a field, that holds a reference to the method
190
// to be called.
191
JiapiField field = null;
192             try {
193                 field = clazz.getDeclaredField(jiapiFieldName);
194             }
195             catch (NoSuchFieldException JavaDoc nsfe) {
196                 throw new JiapiRuntimeException("No such field: " + nsfe.getMessage());
197             }
198
199             il.add(factory.getField(field));
200
201             if (Modifier.isStatic(currentMethodModifiers)) {
202                 il.add(factory.pushConstant(clazz.getName()));
203             }
204             else {
205                 il.add(factory.pushThis()); // First argument is 'this'
206
}
207         }
208         else {
209             log.debug("Hook is static");
210             // On static methods, First argument is name of the current class.
211
// This should be Class object.
212
// Runtime.forName(clazz.getName())
213
// factory.forName(clazz.getName())
214
il.add(factory.pushConstant(clazz.getName()));
215         }
216
217         // Skip first param (source object made above)
218
for (int i = 1; i < hookParams.length; i++) {
219             if (hookParams[i].equals(String JavaDoc.class)) {
220                 // --- target name ---
221
String JavaDoc targetName = instrumentation.getTargetName();
222                 if (targetName == null) {
223                     targetName = "???";
224                 }
225
226                 il.add(factory.pushConstant(targetName));
227             }
228             else if (hookParams[i].equals(Object JavaDoc.class)) {
229                 // --- target Object ---
230
InstructionList targetCode = instrumentation.getTargetCode();
231                 if (targetCode != null) {
232                     log.debug("Got target code: " + targetCode);
233                     il.add(targetCode);
234                 }
235                 else {
236                     log.debug("No target code");
237                     il.add(factory.pushNull());
238                 }
239             }
240             else if (hookParams[i].equals(Object JavaDoc[].class)) {
241                 // --- target Object[] ---
242
log.warn("target arguments are not supported");
243                 il.add(factory.pushNull());
244             }
245             else {
246                 log.error("Invalid Hook method: " + hookMethod);
247             }
248         }
249
250         Loader l = new Loader();
251         try {
252             JiapiClass hClass = l.loadClass(hookClass.getName());
253             Class JavaDoc[] hpTypes = hookMethod.getParameterTypes();
254             String JavaDoc[] pTypes = new String JavaDoc[hpTypes.length];
255             
256             for (int i = 0; i < pTypes.length; i++) {
257                 pTypes[i] = hpTypes[i].getName();
258             }
259             
260             JiapiMethod hMethod =hClass.getDeclaredMethod(hookMethod.getName(),
261                                                           pTypes);
262             il.add(factory.invoke(hMethod));
263         }
264         catch(Exception JavaDoc e) {
265             log.error("Failed to add invoke instruction: " + e);
266         }
267     }
268
269     // Removed static below. -- mcr70 -- 02032004
270
private /*static*/ HashMap JavaDoc preInstrumentations = new HashMap JavaDoc();
271     private /*static*/ HashMap JavaDoc postInstrumentations = new HashMap JavaDoc();
272
273     /**
274      * Creates a chain which is used to add a new field to a target
275      * class. This field will hold a reference to an instance for
276      * which the calls will be made. On static calls there's no
277      * need for this field.
278      *
279      * @return A chain, that will be processed before current Instrumentor.
280      */

281     public InstrumentorChain preInstrument() {
282         if (!isDynamic) {
283             log.info("Will not pre instrument for static calls");
284             // Static calls don't need any helper instrumentors.
285
return null;
286         }
287
288         JiapiClass c = getCurrentClass();
289
290         // Make sure pre instrumentation is done only once per JiapiClass
291
if (preInstrumentations.containsKey(c)) {
292             return null;
293         }
294         else {
295             preInstrumentations.put(c, null);
296         }
297         
298         int modifiers = c.getModifiers();
299         if (Modifier.isInterface(modifiers)) {
300             log.info("Will not pre instrument interface " + c.getName());
301             return null;
302         }
303
304         preChain = new InstrumentorChain();
305
306         // Add an instrumentor which will create a new field to hold
307
// reference to hook instance in dynamic calls.
308
preChain.add(new CreateFieldInstrumentor(Modifier.PUBLIC +
309                                                  Modifier.STATIC,
310                                                  hookClass.getName(),
311                                                  jiapiFieldName));
312         
313 // preChain.add(new CreateMethodInstrumentor(Modifier.STATIC,
314
// "<clinit>"));
315

316 // preChain.add(new HeadInstrumentor());
317

318 // // Add an instrumentor which will initialize the field in
319
// // a static initializer.
320
// preChain.add(new FieldAssignInstrumentor(jiapiFieldName,
321
// hook.getInstance()));
322

323         return preChain;
324     }
325
326
327     /**
328      * Creates a chain which properly initializes the field at
329      * static initializer.
330      */

331     public InstrumentorChain postInstrument() {
332         //if (true) return null;
333
if (!isDynamic) {
334             // Static calls don't need any helper instrumentors.
335
return null;
336         }
337
338         JiapiClass c = getCurrentClass();
339         // Make sure pre instrumentation is done only once per JiapiClass
340
if (postInstrumentations.containsKey(c)) {
341             return null;
342         }
343         else {
344             postInstrumentations.put(c, null);
345         }
346
347         int modifiers = c.getModifiers();
348         if (Modifier.isInterface(modifiers)) {
349             log.info("Will not post instrument interface " + c.getName());
350             return null;
351         }
352
353         postChain = new InstrumentorChain();
354
355         // Add an instrumentor which will create a static initializer
356
// if it doesn't exist yet.
357
// NOTE! Mode must be FORWARD_NEW:
358
postChain.add(new CreateMethodInstrumentor(Modifier.STATIC,
359                                                    "<clinit>"));
360         
361         postChain.add(new HeadInstrumentor());
362         
363         // Add an instrumentor which will initialize the field in
364
// a static initializer.
365
postChain.add(new FieldAssignInstrumentor(jiapiFieldName,
366                                                   hook.getInstance()));
367         
368         return postChain;
369     }
370     
371
372     /**
373      * Sets the for which the method calls are delegated at runtime.
374      * @param hook A Hook to be called by this patch.
375      */

376     public void setHook(Hook hook) {
377         this.hook = hook;
378         this.hookMethod = hook.getHookMethod();
379         this.hookClass = hookMethod.getDeclaringClass();
380
381         this.jiapiFieldName = hookClass.getName().replace('.', '_') +
382             "_" + hook.getInstance().hashCode();
383         log.debug("Jiapi field name is '" + jiapiFieldName + "'");
384
385         this.isDynamic = !Modifier.isStatic(hookMethod.getModifiers());
386     }
387     
388     /**
389      * Get the associated Hook.
390      * @return Hook
391      */

392     public Hook getHook() {
393         return hook;
394     }
395
396
397
398     public String JavaDoc toString() {
399         return getClass().getName() + "#" + hook.getInstance().toString();//getClass().getName();
400
}
401 }
402
Popular Tags