KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > fractal > julia > asm > SimpleCodeGenerator


1 /***
2  * Julia: France Telecom's implementation of the Fractal API
3  * Copyright (C) 2001-2002 France Telecom R&D
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Contact: Eric.Bruneton@rd.francetelecom.com
20  *
21  * Author: Eric Bruneton
22  */

23
24 package org.objectweb.fractal.julia.asm;
25
26 import org.objectweb.fractal.julia.InitializationContext;
27 import org.objectweb.fractal.julia.loader.Initializable;
28 import org.objectweb.fractal.julia.loader.Tree;
29
30 import org.objectweb.asm.CodeVisitor;
31 import org.objectweb.asm.Type;
32
33 import java.lang.reflect.Method JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.List JavaDoc;
36
37 /**
38  * A {@link CodeGenerator} to generate pre and post code
39  * to call an associated controller object. This abstract code generator can
40  * be used to easily generate interceptors of the following form, without
41  * knowing the Java bytecode instructions and ASM:
42  *
43  * <center>
44  * <img SRC="../../../../../../figures/interceptor.gif" height=313 width=417><p>
45  * <b>Figure 1: an interceptor and an associated controller object (see also
46  * <a HREF="../../../../../overview-summary.html#4">here</a>)</b>
47  * </center>
48  *
49  * <p>where the interceptor calls a <tt>void</tt> <i>prem</i> <tt>(String
50  * s)</tt> method and a <tt>void</tt> <i>postm</i> <tt>(String s)</tt> method on
51  * the controller object before and after each intercepted method. This abstract
52  * code generator can be configured in many ways:
53  * <ul>
54  * <li>The name <i>N</i> (see the above figure) can be configured with the
55  * {@link #getControllerInterfaceName getControllerInterfaceName} method.</li>
56  * <li>The names of the <i>prem</i> and <i>postm</i> methods can be configured
57  * with the {@link #getPreMethodName getPreMethodName} and {@link
58  * #getPostMethodName getPostMethodName} methods. These methods must be defined
59  * in the <i>C</i> class. They can also be declared in the <tt>T</tt>
60  * interface, but this is not mandatory.</li>
61  * <li>the <i>prem</i> and <i>postm</i> methods take as argument a String that
62  * is computed from the description of the intercepted method by the {@link
63  * #getMethodName getMethodName} method.</li>
64  * <li>a context can be returned by the <i>prem</i> method and passed back to
65  * the <i>postm</i> method (this can be useful to avoid mixin the start of a
66  * method call with the end of another, concurrent, method call). The type of
67  * this context can be configured with the {@link #getContextType
68  * getContextType} method. It can be a primitive type, an object type, or void
69  * if no context is required. If a context is used, then the <i>prem</i> and
70  * <i>postm</i> methods defined in <i>C</i> must have the following
71  * signatures (where <tt>I</tt> is the type of the context): <tt>I prem
72  * (String s)</tt> and <tt>void postm (String s, I ctxt)</tt>.</li>
73  * <li>the name of fractal interface to which the interceptor is associated can
74  * be passed as parameter in the <i>prem</i> and <i>postm</i> methods. For
75  * this you just need to override the {@link #needsInterfaceName
76  * needsInterfaceName} method, so that it returns <tt>true</tt>. The <i>prem</i>
77  * and <i>postm</i> methods must then have an additional parameter of type
78  * <tt>String</tt>: <tt>I prem (String itf, String s)</tt> and <tt>void postm
79  * (String itf, String s, I ctxt)</tt>.</li>
80  * <li>the target object of the intercepted method call can be passed as
81  * parameter in the <i>prem</i> and <i>postm</i> methods. For
82  * this you just need to override the {@link #needsTargetObject
83  * needsTargetObject} method, so that it returns <tt>true</tt>. The <i>prem</i>
84  * and <i>postm</i> methods must then have an additional parameter of type
85  * <tt>Object</tt>: <tt>I prem (Object target, String s)</tt> and <tt>void postm
86  * (Object target, String s, I ctxt)</tt> (if the previous option is also
87  * enabled, the parameters must be placed in the following order: <tt>I prem
88  * (String itf, Object target, String s)</tt> and <tt>void postm (String itf,
89  * Object target, String s, I ctxt)</tt>).</li>
90  * <li>the {@link #getInterceptionType getInterceptionType} method can be
91  * overriden to choose between three type of pre/post semantics. See {@link
92  * AbstractCodeGenerator}.</li>
93  * <li>the {@link #intercept intercept} method can be overriden to intercept
94  * only some specific methods, while leaving the other unchanged.</li>
95  * </ul>
96  *
97  * <p>The code adapters returned by the {@link #generateInterceptionCode
98  * generateInterceptionCode} method (see {@link CodeGenerator})
99  * transform the original methods into methods of the following form (assuming
100  * a context is used):
101  *
102  * <p><pre>
103  * <i>method-signature</i> {
104  * delegate.<i>prem</i>(<i>id</i>);
105  * // original method code
106  * }
107  * </pre>
108  *
109  * if {@link #getInterceptionType getInterceptionType} returns
110  * {@link InterceptorCodeAdapter#EMPTY EMPTY},
111  *
112  * <p><pre>
113  * <i>method-signature</i> {
114  * <i>return-type</i> result;
115  * <i>I</i> context = delegate.<i>prem</i>(<i>id</i>);
116  * // original method code, where returns are replaced with gotos
117  * delegate.<i>postm</i>(<i>id</i>, context);
118  * return result;
119  * }
120  * </pre>
121  *
122  * if {@link #getInterceptionType getInterceptionType} returns
123  * {@link InterceptorCodeAdapter#NORMAL NORMAL},
124  *
125  * <p><pre>
126  * <i>method signature</i> {
127  * <i>I</i> context = delegate.<i>prem</i>(<i>id</i>);
128  * try {
129  * // original method code
130  * } finally {
131  * delegate.<i>postm</i>(<i>id</i>, context);
132  * }
133  * }
134  * </pre>
135  *
136  * if {@link #getInterceptionType getInterceptionType} returns
137  * {@link InterceptorCodeAdapter#FINALLY FINALLY}.
138  *
139  * <p>The {@link #generateInitCode generateInitCode} method adds a
140  * <tt>delegate</tt> field to the interceptor class, and adds a code fragment of
141  * the following form to the generated {@link #generateInitCode
142  * generateInitCode} method (recall that <i>N</i> can be configured with the
143  * {@link #getControllerInterfaceName getControllerInterfaceName} method):
144  *
145  * <p><pre>
146  * delegate = (...)ic.getFcInterface(<i>N</i>);
147  * </pre>
148  *
149  * Finally this code generator takes into account the Julia
150  * <a HREF="../../../../../overview-summary.html#5.1">optimizations options</a>.
151  * So, for example, if the interceptors and controller objects are merged, the
152  * <tt>delegate</tt> field is not generated, and <tt>this</tt> is used instead.
153  */

154
155 public abstract class SimpleCodeGenerator
156   extends AbstractCodeGenerator
157   implements Initializable
158 {
159
160   /**
161    * The arguments used to initialize this object. These arguments are the list
162    * of interface names, corresponding to the list of interface types in
163    * icg.interfaces. May be <tt>null</tt>.
164    */

165
166   private Tree args;
167
168   /**
169    * The interceptor class generator to which this code generator belongs.
170    */

171
172   private InterceptorClassGenerator icg;
173
174   /**
175    * The name of the 'delegate' field.
176    */

177
178   private String JavaDoc delegateFieldName;
179
180   /**
181    * The descriptor of the 'delegate' field.
182    */

183
184   private String JavaDoc delegateFieldDesc;
185
186   /**
187    * The name of the owner class of the 'delegate' field.
188    */

189
190   private String JavaDoc owner;
191
192   // -------------------------------------------------------------------------
193
// Implementation of the Init interface
194
// -------------------------------------------------------------------------
195

196   public void initialize (final Tree args) {
197     this.args = args.getSubTree(0);
198   }
199
200   // -------------------------------------------------------------------------
201
// Overriden methods
202
// -------------------------------------------------------------------------
203

204   public int init (final InterceptorClassGenerator icg) {
205     this.icg = icg;
206     return IN;
207   }
208
209   public void generateInitCode (final CodeVisitor cv)
210     throws ClassGenerationException
211   {
212     // computes the internal name of the controller class
213
// that contains the needed 'preMethod' method
214
String JavaDoc owner = null;
215     for (int i = 0; i < icg.controllerClasses.length; ++i) {
216       Class JavaDoc c = icg.controllerClasses[i];
217       List JavaDoc params = new ArrayList JavaDoc();
218       if (needsInterfaceName()) {
219         params.add(String JavaDoc.class);
220       }
221       if (needsTargetObject()) {
222         params.add(Object JavaDoc.class);
223       }
224       params.add(String JavaDoc.class);
225       Class JavaDoc[] paramClasses = (Class JavaDoc[])params.toArray(new Class JavaDoc[params.size()]);
226       try {
227         c.getMethod(getPreMethodName(), paramClasses);
228         owner = c.getName();
229         break;
230       } catch (Exception JavaDoc e) {
231       }
232     }
233     if (owner == null) {
234       throw new ClassGenerationException(
235         null,
236         icg.args.toString(),
237         "Cannot find a controller class providing a '" +
238         getPreMethodName() + "' method with the good arguments");
239     }
240     owner = owner.replace('.', '/');
241
242     int hashcode = getControllerInterfaceName().hashCode();
243     delegateFieldName = "d" + Integer.toHexString(hashcode);
244     delegateFieldDesc = "L" + owner + ";";
245
246     icg.cw.visitField(
247       ACC_PRIVATE, delegateFieldName, delegateFieldDesc, null, null);
248
249     cv.visitVarInsn(ALOAD, 0);
250     cv.visitVarInsn(ALOAD, 1);
251     cv.visitLdcInsn(getControllerInterfaceName());
252     cv.visitMethodInsn(
253       INVOKEVIRTUAL,
254       Type.getInternalName(InitializationContext.class),
255       "getInterface",
256       "(" + Type.getDescriptor(String JavaDoc.class) + ")" +
257         Type.getDescriptor(Object JavaDoc.class));
258     cv.visitTypeInsn(CHECKCAST, owner);
259     cv.visitFieldInsn(PUTFIELD, icg.name, delegateFieldName, delegateFieldDesc);
260   }
261
262   protected int getInterceptionCodeFormals (final Method JavaDoc m) {
263     // local variable to store the context, if a context is used
264
return AbstractClassGenerator.getSize(getContextType());
265   }
266
267   public void generateCloneCode (final CodeVisitor cv) {
268     cv.visitVarInsn(ALOAD, 1);
269     cv.visitVarInsn(ALOAD, 0);
270     cv.visitFieldInsn(GETFIELD, icg.name, delegateFieldName, delegateFieldDesc);
271     cv.visitFieldInsn(PUTFIELD, icg.name, delegateFieldName, delegateFieldDesc);
272   }
273
274   // -------------------------------------------------------------------------
275
// Implementation of inherited abstract methods
276
// -------------------------------------------------------------------------
277

278   protected void generateInterceptionCodeBlock (
279     final Method JavaDoc m,
280     final boolean pre,
281     final CodeVisitor cv,
282     final int formals)
283   {
284     Class JavaDoc c = getContextType();
285     String JavaDoc desc = Type.getDescriptor(c);
286
287     // pushes the reference of the controller object associated to the
288
// interceptor. This reference is stored in the 'delegate' field, unless
289
// the controller and interceptor classes are merged (in this case the
290
// reference of the controller object is the reference of the interceptor,
291
// i.e., 'this')
292
cv.visitVarInsn(ALOAD, 0);
293     // if controller and interceptor classes are merged, the generateInitCode
294
// method is not called, and the delegateFieldName is therefore null. In
295
// this case we do not load the value of the 'delegate' field, which does
296
// not exist.
297
if (delegateFieldName != null) {
298       cv.visitFieldInsn(
299         GETFIELD, icg.name, delegateFieldName, delegateFieldDesc);
300     }
301
302     if (needsInterfaceName()) {
303       String JavaDoc name = getInterfaceName(m);
304       if (name == null) {
305         cv.visitInsn(ACONST_NULL);
306       } else {
307         cv.visitLdcInsn(name);
308       }
309     }
310
311     if (needsTargetObject()) {
312       // generates code to push the reference of the target object
313
cv.visitVarInsn(ALOAD, 0);
314       cv.visitFieldInsn(
315         GETFIELD, icg.name, icg.implFieldName, icg.implFieldDesc);
316     }
317
318     // generates code to push the name of the intercepted method
319
cv.visitLdcInsn(getMethodName(m));
320
321     // if we are generating the post code block, and if a context is passed from
322
// the pre method to the post method, then we push this context, which has
323
// been stored in the 'formals' local variable in the pre code block.
324
if (!pre && c != Void.TYPE) {
325       cv.visitVarInsn(
326         ILOAD + AbstractClassGenerator.getOpcodeOffset(c), formals);
327     }
328
329     // we then generate the code to call the pre or post method
330

331     // we first compute the name of the owner class of this method.
332
if (owner == null) {
333       if (delegateFieldDesc == null) {
334         owner = icg.name;
335       } else {
336         owner = delegateFieldDesc.substring(1, delegateFieldDesc.length() - 1);
337       }
338     }
339
340     // we then compute the name of the pre or post method itself
341
String JavaDoc name = pre ? getPreMethodName() : getPostMethodName();
342
343     // and we compute the signature of this method
344
if (pre) {
345       desc = "Ljava/lang/String;)" + desc;
346     } else if (c != Void.TYPE) {
347       desc = "Ljava/lang/String;" + desc + ")V";
348     } else {
349       desc = "Ljava/lang/String;)V";
350     }
351     if (needsTargetObject()) {
352       desc = "Ljava/lang/Object;" + desc;
353     }
354     if (needsInterfaceName()) {
355       desc = "Ljava/lang/String;" + desc;
356     }
357
358     // we can finally generate the code to invoke the pre or post method
359
cv.visitMethodInsn(INVOKEVIRTUAL, owner, name, "(" + desc);
360
361     // if we are generating the pre code block, and if a context is passed from
362
// the pre method to the post method, then we store this context, which has
363
// in the 'formals' local variable.
364
if (pre && c != Void.TYPE) {
365       cv.visitVarInsn(
366         ISTORE + AbstractClassGenerator.getOpcodeOffset(c), formals);
367     }
368   }
369
370   // -------------------------------------------------------------------------
371
// Utility methods
372
// -------------------------------------------------------------------------
373

374   /**
375    * Returns the name of the interface provided by the controller object to be
376    * associated to the interceptor.
377    *
378    * @return the name of the interface provided by the controller object to be
379    * associated to the interceptor (called <i>N</i> in the above figure).
380    */

381
382   protected abstract String JavaDoc getControllerInterfaceName ();
383
384   /**
385    * Returns the name of the method to be called on the associated controller
386    * object, before each intercepted call.
387    *
388    * @return the name of the method to be called on the associated controller
389    * object, before each intercepted call (called <i>prem</i> above).
390    */

391
392   protected abstract String JavaDoc getPreMethodName ();
393
394   /**
395    * Returns the name of the method to be called on the associated controller
396    * object, after each intercepted call.
397    *
398    * @return the name of the method to be called on the associated controller
399    * object, after each intercepted call (called <i>postm</i> above).
400    */

401
402   protected abstract String JavaDoc getPostMethodName ();
403
404   /**
405    * Returns the type of the context to be passed from the "pre method" to the
406    * "post method".
407    *
408    * @return the type of the context to be passed from the "pre method" to the
409    * "post method" (called <i>I</i> above).
410    */

411
412   protected abstract Class JavaDoc getContextType ();
413
414   /**
415    * Returns the String to be passed as argument to the pre and post methods,
416    * for the given method.
417    *
418    * @param m a method object.
419    * @return the String to be passed as argument to the pre and post methods,
420    * for the given method.
421    */

422
423   protected abstract String JavaDoc getMethodName (Method JavaDoc m);
424
425   /**
426    * Returns <tt>true</tt> if the target interface name must be passed as
427    * argument to the pre and post methods. The default implementation of this
428    * method returns <tt>false</tt>.
429    *
430    * @return <tt>true</tt> if the name of the fractal interface associated to
431    * the generated interceptor must be passed as argument in the pre and
432    * post methods.
433    */

434
435   protected boolean needsInterfaceName () {
436     return false;
437   }
438
439   /**
440    * Returns the name of the fractal interface in which the given method is
441    * defined.
442    *
443    * @param m a method.
444    * @return the name of the fractal interface in which the given method is
445    * defined.
446    */

447
448   private String JavaDoc getInterfaceName (Method JavaDoc m) {
449     if (args != null) {
450       String JavaDoc desc = m.getName()+Type.getMethodDescriptor(m);
451       List JavaDoc itfs = icg.interfaces;
452       for (int i = 0; i < itfs.size(); ++i) {
453         String JavaDoc itf = ((String JavaDoc)itfs.get(i)).replace('/', '.');
454         Class JavaDoc c;
455         try {
456           c = icg.loader.loadClass(itf, icg.classLoader);
457         } catch (ClassNotFoundException JavaDoc e) {
458           continue;
459         }
460         Method JavaDoc[] meths = c.getMethods();
461         for (int j = 0; j < meths.length; ++j) {
462           Method JavaDoc meth = meths[j];
463           if (desc.equals(meth.getName()+Type.getMethodDescriptor(meth))) {
464             return args.getSubTree(i).toString();
465           }
466         }
467       }
468     }
469     return null;
470   }
471
472   /**
473    * Returns <tt>true</tt> if the target object must be passed as argument
474    * to the pre and post methods. The default implementation of this method
475    * returns <tt>false</tt>.
476    *
477    * @return <tt>true</tt> if the target object of intercepted method calls must
478    * be passed as argument in the pre and post methods.
479    */

480
481   protected boolean needsTargetObject () {
482     return false;
483   }
484 }
485
Popular Tags