KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > transform > inlining > weaver > AddMixinMethodsVisitor


1 /**************************************************************************************
2  * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3  * http://aspectwerkz.codehaus.org *
4  * ---------------------------------------------------------------------------------- *
5  * The software in this package is published under the terms of the LGPL license *
6  * a copy of which has been included with this distribution in the license.txt file. *
7  **************************************************************************************/

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

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

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

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

107     private void addMixinMembers() {
108         int index = 0;
109         for (Iterator JavaDoc it = m_ctx.getDefinitions().iterator(); it.hasNext();) {
110             List JavaDoc mixinDefs = ((SystemDefinition) it.next()).getMixinDefinitions(m_expressionContext);
111
112             // check for method clashes
113
Set JavaDoc interfaceSet = new HashSet JavaDoc();
114             for (Iterator JavaDoc it2 = mixinDefs.iterator(); it2.hasNext();) {
115                 interfaceSet.addAll(((MixinDefinition) it2.next()).getInterfaceClassNames());
116             }
117             if (ClassInfoHelper.hasMethodClash(interfaceSet, m_ctx.getLoader())) {
118                 return;
119             }
120
121             for (Iterator JavaDoc it2 = mixinDefs.iterator(); it2.hasNext();) {
122                 final MixinDefinition mixinDef = (MixinDefinition) it2.next();
123                 final ClassInfo mixinImpl = mixinDef.getMixinImpl();
124                 final DeploymentModel deploymentModel = mixinDef.getDeploymentModel();
125
126                 if (m_mixinFields.containsKey(mixinImpl)) {
127                     continue;
128                 }
129                 final MixinFieldInfo fieldInfo = new MixinFieldInfo();
130                 fieldInfo.fieldName = MIXIN_FIELD_NAME + index;
131                 fieldInfo.mixinClassInfo = mixinImpl;
132
133                 addMixinField(fieldInfo, deploymentModel, mixinDef);
134                 addMixinMethods(fieldInfo, mixinDef);
135
136                 index++;
137                 m_isAdvised = true;
138             }
139         }
140     }
141
142     /**
143      * Appends mixin instantiation to the clinit method and/or init method.
144      *
145      * @param access
146      * @param name
147      * @param desc
148      * @param exceptions
149      * @param attrs
150      * @return
151      */

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

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

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

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

280     private void addMixinField(final MixinFieldInfo fieldInfo,
281                                final DeploymentModel deploymentModel,
282                                final MixinDefinition mixinDef) {
283         final String JavaDoc signature = fieldInfo.mixinClassInfo.getSignature();
284         int modifiers = 0;
285         if (deploymentModel.equals(DeploymentModel.PER_CLASS) || deploymentModel.equals(DeploymentModel.PER_JVM)) {
286             fieldInfo.isStatic = true;
287             fieldInfo.isPerJVM = deploymentModel.equals(DeploymentModel.PER_JVM);
288             modifiers = ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC;
289         } else if (deploymentModel.equals(DeploymentModel.PER_INSTANCE)) {
290             fieldInfo.isStatic = false;
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             CodeVisitor 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 CodeAdapter {
394
395         public PrependToClinitMethodCodeAdapter(final CodeVisitor 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 CodeAdapter {
412
413         public AppendToInitMethodCodeAdapter(final CodeVisitor 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(cv, 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