KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > aspectwerkz > transform > inlining > weaver > AddMixinMethodsVisitor


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.aspectwerkz.transform.inlining.weaver;
5
6 import java.util.HashMap JavaDoc;
7 import java.util.HashSet JavaDoc;
8 import java.util.Iterator JavaDoc;
9 import java.util.List JavaDoc;
10 import java.util.Map JavaDoc;
11 import java.util.Set JavaDoc;
12
13 import com.tc.asm.ClassAdapter;
14 import com.tc.asm.ClassVisitor;
15 import com.tc.asm.MethodAdapter;
16 import com.tc.asm.MethodVisitor;
17 import com.tc.asm.Type;
18
19 import com.tc.aspectwerkz.DeploymentModel;
20 import com.tc.aspectwerkz.expression.PointcutType;
21 import com.tc.aspectwerkz.expression.ExpressionContext;
22 import com.tc.aspectwerkz.reflect.ClassInfo;
23 import com.tc.aspectwerkz.reflect.FieldInfo;
24 import com.tc.aspectwerkz.reflect.MethodInfo;
25 import com.tc.aspectwerkz.definition.MixinDefinition;
26 import com.tc.aspectwerkz.definition.SystemDefinition;
27 import com.tc.aspectwerkz.exception.DefinitionException;
28 import com.tc.aspectwerkz.transform.InstrumentationContext;
29 import com.tc.aspectwerkz.transform.TransformationConstants;
30 import com.tc.aspectwerkz.transform.inlining.AsmHelper;
31
32 /**
33  * Adds mixin methods and fields to hold mixin instances to the target class.
34  *
35  * @author <a HREF="mailto:jboner@codehaus.org">Jonas BonŽr </a>
36  */

37 public class AddMixinMethodsVisitor extends ClassAdapter implements TransformationConstants {
38
39   private final InstrumentationContext m_ctx;
40   private String JavaDoc m_declaringTypeName;
41   private final ClassInfo m_classInfo;
42   private final Set JavaDoc m_addedMethods;
43   private ExpressionContext m_expressionContext;
44   private boolean m_hasClinit = false;
45   private Map JavaDoc m_mixinFields;
46   private boolean m_isAdvised = false;
47
48   /**
49    * Creates a new class adapter.
50    *
51    * @param cv
52    * @param classInfo
53    * @param ctx
54    * @param addedMethods
55    */

56   public AddMixinMethodsVisitor(final ClassVisitor cv,
57                                 final ClassInfo classInfo,
58                                 final InstrumentationContext ctx,
59                                 final Set JavaDoc addedMethods) {
60     super(cv);
61     m_classInfo = classInfo;
62     m_ctx = (InstrumentationContext) ctx;
63     m_addedMethods = addedMethods;
64     m_expressionContext = new ExpressionContext(PointcutType.WITHIN, m_classInfo, m_classInfo);
65   }
66
67   /**
68    * Visits the class.
69    *
70    * @param access
71    * @param name
72    * @param signature
73    * @param superName
74    * @param interfaces
75    */

76   public void visit(final int version,
77                     final int access,
78                     final String JavaDoc name,
79                     final String JavaDoc signature,
80                     final String JavaDoc superName,
81                     final String JavaDoc[] interfaces) {
82     ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, m_classInfo, m_classInfo);
83     if (!classFilter(m_classInfo, ctx, m_ctx.getDefinitions())) {
84       m_declaringTypeName = name;
85       m_mixinFields = new HashMap JavaDoc();
86
87       // populate with fields already present for mixins from previous weaving
88
for (int i = 0; i < m_classInfo.getFields().length; i++) {
89         FieldInfo fieldInfo = m_classInfo.getFields()[i];
90         if (fieldInfo.getName().startsWith(MIXIN_FIELD_NAME)) {
91           m_mixinFields.put(fieldInfo.getType(), fieldInfo);
92         }
93       }
94
95       // add fields and method for (not already there) mixins
96
addMixinMembers();
97     }
98     super.visit(version, access, name, signature, superName, interfaces);
99   }
100
101   /**
102    * Adds mixin fields and methods to the target class.
103    */

104   private void addMixinMembers() {
105     int index = 0;
106     for (Iterator JavaDoc it = m_ctx.getDefinitions().iterator(); it.hasNext();) {
107       List JavaDoc mixinDefs = ((SystemDefinition) it.next()).getMixinDefinitions(m_expressionContext);
108
109       // check for method clashes
110
Set JavaDoc interfaceSet = new HashSet JavaDoc();
111       for (Iterator JavaDoc it2 = mixinDefs.iterator(); it2.hasNext();) {
112         interfaceSet.addAll(((MixinDefinition) it2.next()).getInterfaceClassNames());
113       }
114       //TODO refactor to handle precedence injection
115
// if (ClassInfoHelper.hasMethodClash(interfaceSet, m_ctx.getLoader())) {
116
// return;
117
// }
118

119       for (Iterator JavaDoc it2 = mixinDefs.iterator(); it2.hasNext();) {
120         final MixinDefinition mixinDef = (MixinDefinition) it2.next();
121         final ClassInfo mixinImpl = mixinDef.getMixinImpl();
122         final DeploymentModel deploymentModel = mixinDef.getDeploymentModel();
123
124         if (m_mixinFields.containsKey(mixinImpl)) {
125           continue;
126         }
127         final MixinFieldInfo fieldInfo = new MixinFieldInfo();
128         fieldInfo.fieldName = MIXIN_FIELD_NAME + index;
129         fieldInfo.mixinClassInfo = mixinImpl;
130
131         addMixinField(fieldInfo, deploymentModel, mixinDef);
132         addMixinMethods(fieldInfo, mixinDef);
133
134         index++;
135         m_isAdvised = true;
136       }
137     }
138   }
139
140   /**
141    * Appends mixin instantiation to the clinit method and/or init method.
142    *
143    * @param access
144    * @param name
145    * @param desc
146    * @param signature
147    * @param exceptions
148    * @return
149    */

150   public MethodVisitor visitMethod(final int access,
151                                    final String JavaDoc name,
152                                    final String JavaDoc desc,
153                                    final String JavaDoc signature,
154                                    final String JavaDoc[] exceptions) {
155     if (m_isAdvised) {
156       if (name.equals(CLINIT_METHOD_NAME)) {
157         m_hasClinit = true;
158         MethodVisitor mv = new PrependToClinitMethodCodeAdapter(
159                 cv.visitMethod(access, name, desc, signature, exceptions)
160         );
161         mv.visitMaxs(0, 0);
162         return mv;
163       } else if (name.equals(INIT_METHOD_NAME)) {
164         MethodVisitor mv = new AppendToInitMethodCodeAdapter(
165                 cv.visitMethod(access, name, desc, signature, exceptions)
166         );
167         mv.visitMaxs(0, 0);
168         return mv;
169       }
170     }
171     return super.visitMethod(access, name, desc, signature, exceptions);
172   }
173
174   /**
175    * Creates a new clinit method and adds mixin instantiation if it does not exist.
176    */

177   public void visitEnd() {
178     if (m_isAdvised && !m_hasClinit) {
179       // add the <clinit> method
180
MethodVisitor mv = cv.visitMethod(
181               ACC_STATIC, CLINIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE, null, null
182       );
183       for (Iterator JavaDoc i4 = m_mixinFields.values().iterator(); i4.hasNext();) {
184         MixinFieldInfo fieldInfo = (MixinFieldInfo) i4.next();
185         if (fieldInfo.isStatic) {
186           initializeStaticMixinField(mv, fieldInfo);
187         }
188       }
189
190       mv.visitInsn(RETURN);
191       mv.visitMaxs(0, 0);
192     }
193     super.visitEnd();
194   }
195
196   /**
197    * Initializes a static mixin field.
198    *
199    * @param mv
200    * @param fieldInfo
201    */

202   private void initializeStaticMixinField(final MethodVisitor mv, final MixinFieldInfo fieldInfo) {
203     mv.visitLdcInsn(fieldInfo.mixinClassInfo.getName().replace('/', '.'));
204     if (fieldInfo.isPerJVM) {
205       mv.visitFieldInsn(
206               GETSTATIC,
207               m_declaringTypeName,
208               TARGET_CLASS_FIELD_NAME,
209               CLASS_CLASS_SIGNATURE
210       );
211       mv.visitMethodInsn(
212               INVOKEVIRTUAL,
213               CLASS_CLASS,
214               GETCLASSLOADER_METHOD_NAME,
215               CLASS_CLASS_GETCLASSLOADER_METHOD_SIGNATURE
216       );
217       mv.visitMethodInsn(
218               INVOKESTATIC,
219               MIXINS_CLASS_NAME,
220               MIXIN_OF_METHOD_NAME,
221               MIXIN_OF_METHOD_PER_JVM_SIGNATURE
222       );
223     } else {
224       mv.visitFieldInsn(
225               GETSTATIC,
226               m_declaringTypeName,
227               TARGET_CLASS_FIELD_NAME,
228               CLASS_CLASS_SIGNATURE
229       );
230       mv.visitMethodInsn(
231               INVOKESTATIC,
232               MIXINS_CLASS_NAME,
233               MIXIN_OF_METHOD_NAME,
234               MIXIN_OF_METHOD_PER_CLASS_SIGNATURE
235       );
236     }
237     mv.visitTypeInsn(CHECKCAST, fieldInfo.mixinClassInfo.getName().replace('.', '/'));
238     mv.visitFieldInsn(
239             PUTSTATIC,
240             m_declaringTypeName,
241             fieldInfo.fieldName,
242             fieldInfo.mixinClassInfo.getSignature()
243     );
244   }
245
246   /**
247    * Initializes a member mixin field.
248    *
249    * @param mv
250    * @param fieldInfo
251    */

252   private void initializeMemberMixinField(final MethodVisitor mv, final MixinFieldInfo fieldInfo) {
253     mv.visitVarInsn(ALOAD, 0);
254     mv.visitLdcInsn(fieldInfo.mixinClassInfo.getName().replace('/', '.'));
255     mv.visitVarInsn(ALOAD, 0);
256     mv.visitMethodInsn(
257             INVOKESTATIC,
258             MIXINS_CLASS_NAME,
259             MIXIN_OF_METHOD_NAME,
260             MIXIN_OF_METHOD_PER_INSTANCE_SIGNATURE
261     );
262     mv.visitTypeInsn(CHECKCAST, fieldInfo.mixinClassInfo.getName().replace('.', '/'));
263     mv.visitFieldInsn(
264             PUTFIELD,
265             m_declaringTypeName,
266             fieldInfo.fieldName,
267             fieldInfo.mixinClassInfo.getSignature()
268     );
269   }
270
271   /**
272    * Adds the mixin field to the target class.
273    *
274    * @param fieldInfo
275    * @param deploymentModel
276    * @param mixinDef
277    */

278   private void addMixinField(final MixinFieldInfo fieldInfo,
279                              final DeploymentModel deploymentModel,
280                              final MixinDefinition mixinDef) {
281     final String JavaDoc signature = fieldInfo.mixinClassInfo.getSignature();
282     int modifiers = 0;
283     if (deploymentModel.equals(DeploymentModel.PER_CLASS) || deploymentModel.equals(DeploymentModel.PER_JVM)) {
284       fieldInfo.isStatic = true;
285       fieldInfo.isPerJVM = deploymentModel.equals(DeploymentModel.PER_JVM);
286       modifiers = ACC_PRIVATE + ACC_FINAL + ACC_STATIC;
287 // modifiers = ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC;
288
} else if (deploymentModel.equals(DeploymentModel.PER_INSTANCE)) {
289       fieldInfo.isStatic = false;
290       modifiers = ACC_PRIVATE + ACC_FINAL;
291 // modifiers = ACC_PRIVATE + ACC_FINAL + ACC_SYNTHETIC;
292
} else {
293       throw new DefinitionException(
294               "deployment model [" + mixinDef.getDeploymentModel() +
295                       "] for mixin [" + mixinDef.getMixinImpl().getName() +
296                       "] is not supported"
297       );
298
299     }
300     if (mixinDef.isTransient()) {
301       modifiers += ACC_TRANSIENT;
302     }
303     cv.visitField(modifiers, fieldInfo.fieldName, signature, null, null);
304     m_mixinFields.put(mixinDef.getMixinImpl(), fieldInfo);
305   }
306
307   /**
308    * Adds the mixin methods to the target class.
309    *
310    * @param fieldInfo
311    * @param mixinDef
312    */

313   private void addMixinMethods(final MixinFieldInfo fieldInfo, final MixinDefinition mixinDef) {
314     for (Iterator JavaDoc it3 = mixinDef.getMethodsToIntroduce().iterator(); it3.hasNext();) {
315       MethodInfo methodInfo = (MethodInfo) it3.next();
316       final String JavaDoc methodName = methodInfo.getName();
317       final String JavaDoc methodSignature = methodInfo.getSignature();
318
319       if (m_addedMethods.contains(AlreadyAddedMethodVisitor.getMethodKey(methodName, methodSignature))) {
320         continue;
321       }
322
323       MethodVisitor mv = cv.visitMethod(
324               ACC_PUBLIC + ACC_SYNTHETIC,
325               methodName,
326               methodSignature,
327               null,
328               null
329       );
330       if (fieldInfo.isStatic) {
331         mv.visitFieldInsn(
332                 GETSTATIC,
333                 m_declaringTypeName,
334                 fieldInfo.fieldName,
335                 fieldInfo.mixinClassInfo.getSignature()
336         );
337       } else {
338         mv.visitVarInsn(ALOAD, 0);
339         mv.visitFieldInsn(
340                 GETFIELD,
341                 m_declaringTypeName,
342                 fieldInfo.fieldName,
343                 fieldInfo.mixinClassInfo.getSignature()
344         );
345       }
346       AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(methodSignature), false);
347       mv.visitMethodInsn(
348               INVOKEVIRTUAL,
349               fieldInfo.mixinClassInfo.getName().replace('.', '/'),
350               methodName,
351               methodSignature
352       );
353       AsmHelper.addReturnStatement(mv, Type.getReturnType(methodSignature));
354       mv.visitMaxs(0, 0);
355     }
356   }
357
358   /**
359    * Filters the classes to be transformed.
360    *
361    * @param classInfo the class to filter
362    * @param ctx the context
363    * @param definitions a set with the definitions
364    * @return boolean true if the method should be filtered away
365    */

366   public static boolean classFilter(final ClassInfo classInfo,
367                                     final ExpressionContext ctx,
368                                     final Set JavaDoc definitions) {
369     for (Iterator JavaDoc it = definitions.iterator(); it.hasNext();) {
370       SystemDefinition systemDef = (SystemDefinition) it.next();
371       if (classInfo.isInterface()) {
372         return true;
373       }
374       String JavaDoc className = classInfo.getName().replace('/', '.');
375       if (systemDef.inExcludePackage(className)) {
376         return true;
377       }
378       if (!systemDef.inIncludePackage(className)) {
379         return true;
380       }
381       if (systemDef.hasMixin(ctx)) {
382         return false;
383       }
384     }
385     return true;
386   }
387
388   /**
389    * Adds initialization of static mixin fields to the beginning of the clinit method.
390    *
391    * @author <a HREF="mailto:jboner@codehaus.org">Jonas BonŽr </a>
392    */

393   public class PrependToClinitMethodCodeAdapter extends MethodAdapter {
394
395     public PrependToClinitMethodCodeAdapter(final MethodVisitor ca) {
396       super(ca);
397       for (Iterator JavaDoc i4 = m_mixinFields.values().iterator(); i4.hasNext();) {
398         MixinFieldInfo fieldInfo = (MixinFieldInfo) i4.next();
399         if (fieldInfo.isStatic) {
400           initializeStaticMixinField(ca, fieldInfo);
401         }
402       }
403     }
404   }
405
406   /**
407    * Adds initialization of member mixin fields to end of the init method.
408    *
409    * @author <a HREF="mailto:jboner@codehaus.org">Jonas BonŽr </a>
410    */

411   public class AppendToInitMethodCodeAdapter extends MethodAdapter {
412
413     public AppendToInitMethodCodeAdapter(final MethodVisitor ca) {
414       super(ca);
415     }
416
417     public void visitInsn(final int opcode) {
418       if (opcode == RETURN) {
419         for (Iterator JavaDoc i4 = m_mixinFields.values().iterator(); i4.hasNext();) {
420           MixinFieldInfo fieldInfo = (MixinFieldInfo) i4.next();
421           if (!fieldInfo.isStatic) {
422             initializeMemberMixinField(mv, fieldInfo);
423           }
424         }
425       }
426       super.visitInsn(opcode);
427     }
428   }
429
430   private static class MixinFieldInfo {
431     private String JavaDoc fieldName;
432     private ClassInfo mixinClassInfo;
433     private boolean isStatic;
434     private boolean isPerJVM = false;
435   }
436 }
Popular Tags