View Javadoc

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 org.objectweb.asm.ClassAdapter;
11  import org.objectweb.asm.ClassVisitor;
12  import org.objectweb.asm.CodeVisitor;
13  import org.objectweb.asm.Attribute;
14  import org.objectweb.asm.CodeAdapter;
15  import org.objectweb.asm.Type;
16  import org.objectweb.asm.Label;
17  import org.codehaus.aspectwerkz.definition.SystemDefinition;
18  import org.codehaus.aspectwerkz.expression.ExpressionContext;
19  import org.codehaus.aspectwerkz.expression.PointcutType;
20  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
21  import org.codehaus.aspectwerkz.reflect.ClassInfo;
22  import org.codehaus.aspectwerkz.reflect.FieldInfo;
23  import org.codehaus.aspectwerkz.reflect.MemberInfo;
24  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
25  import org.codehaus.aspectwerkz.transform.Context;
26  import org.codehaus.aspectwerkz.transform.TransformationUtil;
27  import org.codehaus.aspectwerkz.transform.TransformationConstants;
28  import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
29  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
30  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
31  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
32  
33  import java.lang.reflect.Modifier;
34  import java.util.Iterator;
35  import java.util.Set;
36  
37  /***
38   * Instruments method SET and GET join points by replacing PUTFIELD and GETFIELD instructions with invocations
39   * of the compiled join point.
40   *
41   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
42   */
43  public class FieldSetFieldGetVisitor extends ClassAdapter implements TransformationConstants {
44  
45      private final ContextImpl m_ctx;
46      private final ClassLoader m_loader;
47      private final ClassInfo m_callerClassInfo;
48  
49      private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
50  
51      /***
52       * Creates a new instance.
53       *
54       * @param cv
55       * @param loader
56       * @param classInfo
57       * @param ctx
58       */
59      public FieldSetFieldGetVisitor(final ClassVisitor cv,
60                                     final ClassLoader loader,
61                                     final ClassInfo classInfo,
62                                     final Context ctx) {
63          super(cv);
64          m_loader = loader;
65          m_callerClassInfo = classInfo;
66          m_ctx = (ContextImpl) ctx;
67      }
68  
69      /***
70       * Visits the caller methods.
71       *
72       * @param access
73       * @param name
74       * @param desc
75       * @param exceptions
76       * @param attrs
77       * @return
78       */
79      public CodeVisitor visitMethod(final int access,
80                                     final String name,
81                                     final String desc,
82                                     final String[] exceptions,
83                                     final Attribute attrs) {
84  
85          if (name.startsWith(WRAPPER_METHOD_PREFIX)) {
86              return super.visitMethod(access, name, desc, exceptions, attrs);
87          }
88  
89          CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
90          return mv == null ? null : new ReplacePutFieldAndGetFieldInstructionCodeAdapter(
91                  mv,
92                  m_loader,
93                  m_callerClassInfo,
94                  m_ctx.getClassName(),
95                  name,
96                  desc
97          );
98      }
99  
100     /***
101      * Replaces PUTFIELD and GETFIELD instructions with a call to the compiled JoinPoint instance.
102      *
103      * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
104      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
105      */
106     public class ReplacePutFieldAndGetFieldInstructionCodeAdapter extends AfterObjectInitializationCodeAdapter {
107 
108         private final ClassLoader m_loader;
109         private final ClassInfo m_callerClassInfo;
110         private final String m_callerClassName;
111         private final String m_callerMethodName;
112         private final String m_callerMethodDesc;
113         private final MemberInfo m_callerMemberInfo;
114 
115         /***
116          * Creates a new instance.
117          *
118          * @param ca
119          * @param loader
120          * @param callerClassInfo
121          * @param callerClassName
122          * @param callerMethodName
123          * @param callerMethodDesc
124          */
125         public ReplacePutFieldAndGetFieldInstructionCodeAdapter(final CodeVisitor ca,
126                                                                 final ClassLoader loader,
127                                                                 final ClassInfo callerClassInfo,
128                                                                 final String callerClassName,
129                                                                 final String callerMethodName,
130                                                                 final String callerMethodDesc) {
131             super(ca, callerMethodName);
132             m_loader = loader;
133             m_callerClassInfo = callerClassInfo;
134             m_callerClassName = callerClassName;
135             m_callerMethodName = callerMethodName;
136             m_callerMethodDesc = callerMethodDesc;
137 
138             if (CLINIT_METHOD_NAME.equals(m_callerMethodName)) {
139                 m_callerMemberInfo = m_callerClassInfo.staticInitializer();
140             } else if (INIT_METHOD_NAME.equals(m_callerMethodName)) {
141                 int hash = AsmHelper.calculateConstructorHash(m_callerMethodDesc);
142                 m_callerMemberInfo = m_callerClassInfo.getConstructor(hash);
143             } else {
144                 int hash = AsmHelper.calculateMethodHash(m_callerMethodName, m_callerMethodDesc);
145                 m_callerMemberInfo = m_callerClassInfo.getMethod(hash);
146             }
147             if (m_callerMemberInfo == null) {
148                 System.err.println(
149                         "AW::WARNING " +
150                         "metadata structure could not be build for method ["
151                         + m_callerClassInfo.getName().replace('/', '.')
152                         + '.' + m_callerMethodName + ':' + m_callerMethodDesc + ']'
153                 );
154             }
155         }
156 
157         /***
158          * Label
159          *
160          * @param label
161          */
162         public void visitLabel(Label label) {
163             m_lastLabelForLineNumber = label;
164             super.visitLabel(label);
165         }
166 
167         /***
168          * Visits PUTFIELD and GETFIELD instructions.
169          *
170          * @param opcode
171          * @param className
172          * @param fieldName
173          * @param fieldDesc
174          */
175         public void visitFieldInsn(final int opcode,
176                                    final String className,
177                                    final String fieldName,
178                                    final String fieldDesc) {
179 
180             if (className.endsWith(AbstractJoinPointCompiler.JOIN_POINT_CLASS_SUFFIX) ||
181                 fieldName.startsWith(ASPECTWERKZ_PREFIX) ||
182                 fieldName.startsWith(SYNTHETIC_MEMBER_PREFIX) || // synthetic field
183                 fieldName.equals(SERIAL_VERSION_UID_FIELD_NAME) // can have been added by the weaver (not safe)
184             ) {
185                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
186                 return;
187             }
188 
189             // if within ctor, make sure object initialization has been reached
190             if (!m_isObjectInitialized) {
191                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
192                 return;
193             }
194 
195 
196             final Type fieldType = Type.getType(fieldDesc);
197             final int joinPointHash = AsmHelper.calculateFieldHash(fieldName, fieldDesc);
198             final ClassInfo classInfo = AsmClassInfo.getClassInfo(className, m_loader);
199             final FieldInfo fieldInfo = getFieldInfo(classInfo, className, fieldName, fieldDesc, joinPointHash);
200 
201             if (opcode == PUTFIELD || opcode == PUTSTATIC) {
202                 handleFieldModification(fieldInfo, opcode, className, fieldName, fieldDesc, joinPointHash);
203             } else if (opcode == GETFIELD || opcode == GETSTATIC) {
204                 handleFieldAccess(fieldInfo, opcode, className, fieldName, fieldDesc, joinPointHash, fieldType);
205             } else {
206                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
207             }
208         }
209 
210         /***
211          * Handles field access.
212          *
213          * @param fieldInfo
214          * @param opcode
215          * @param className
216          * @param fieldName
217          * @param fieldDesc
218          * @param joinPointHash
219          * @param fieldType
220          */
221         private void handleFieldAccess(final FieldInfo fieldInfo,
222                                        final int opcode,
223                                        final String className,
224                                        final String fieldName,
225                                        final String fieldDesc,
226                                        int joinPointHash,
227                                        final Type fieldType) {
228             if (m_callerMemberInfo == null) {
229                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
230                 return;
231             }
232 
233             ExpressionContext ctx = new ExpressionContext(PointcutType.GET, fieldInfo, m_callerMemberInfo);
234 
235             if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
236                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
237             } else {
238                 m_ctx.markAsAdvised();
239 
240                 String joinPointClassName = TransformationUtil.getJoinPointClassName(
241                         m_callerClassName,
242                         m_callerMethodName,
243                         m_callerMethodDesc,
244                         className,
245                         JoinPointType.FIELD_GET_INT,
246                         joinPointHash
247                 );
248 
249                 // no param to field, so pass a default value to the invoke method
250                 AsmHelper.addDefaultValue(this, fieldType);
251 
252                 // if static context load NULL else 'this'
253                 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
254                     visitInsn(ACONST_NULL);
255                 } else {
256                     visitVarInsn(ALOAD, 0);
257                 }
258 
259                 // add the call to the join point
260                 super.visitMethodInsn(
261                         INVOKESTATIC,
262                         joinPointClassName,
263                         INVOKE_METHOD_NAME,
264                         TransformationUtil.getInvokeSignatureForFieldJoinPoints(
265                                 fieldInfo.getModifiers(), fieldDesc, m_callerClassName, className
266                         )
267                 );
268 
269                 // emit the joinpoint
270                 m_ctx.addEmittedJoinPoint(
271                         new EmittedJoinPoint(
272                                 JoinPointType.FIELD_GET_INT,
273                                 m_callerClassName,
274                                 m_callerMethodName,
275                                 m_callerMethodDesc,
276                                 m_callerMemberInfo.getModifiers(),
277                                 className,
278                                 fieldName,
279                                 fieldDesc,
280                                 fieldInfo.getModifiers(),
281                                 joinPointHash,
282                                 joinPointClassName,
283                                 m_lastLabelForLineNumber
284                         )
285                 );
286             }
287         }
288 
289         /***
290          * Handles field modification.
291          *
292          * @param fieldInfo
293          * @param opcode
294          * @param className
295          * @param fieldName
296          * @param fieldDesc
297          * @param joinPointHash
298          */
299         private void handleFieldModification(final FieldInfo fieldInfo,
300                                              final int opcode,
301                                              final String className,
302                                              final String fieldName,
303                                              final String fieldDesc,
304                                              final int joinPointHash) {
305             if (m_callerMemberInfo == null) {
306                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
307                 return;
308             }
309 
310             ExpressionContext ctx = new ExpressionContext(PointcutType.SET, fieldInfo, m_callerMemberInfo);
311 
312             if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
313                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
314             } else {
315                 m_ctx.markAsAdvised();
316 
317                 String joinPointClassName = TransformationUtil.getJoinPointClassName(
318                         m_callerClassName,
319                         m_callerMethodName,
320                         m_callerMethodDesc,
321                         className,
322                         JoinPointType.FIELD_SET_INT,
323                         joinPointHash
324                 );
325 
326                 // load the caller instance (this), or null if in a static context
327                 // note that callee instance [optional] and args are already on the stack
328                 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
329                     visitInsn(ACONST_NULL);
330                 } else {
331                     visitVarInsn(ALOAD, 0);
332                 }
333 
334                 // add the call to the join point
335                 super.visitMethodInsn(
336                         INVOKESTATIC,
337                         joinPointClassName,
338                         INVOKE_METHOD_NAME,
339                         TransformationUtil.getInvokeSignatureForFieldJoinPoints(
340                                 fieldInfo.getModifiers(), fieldDesc, m_callerClassName, className
341                         )
342                 );
343 
344                 final int sort = Type.getType(fieldDesc).getSort();
345                 if (sort != Type.LONG && sort != Type.DOUBLE) {
346                     super.visitInsn(POP);
347                 } else {
348                     //AW-437
349                     super.visitInsn(POP2);
350                 }
351 
352                 // emit the joinpoint
353                 m_ctx.addEmittedJoinPoint(
354                         new EmittedJoinPoint(
355                                 JoinPointType.FIELD_SET_INT,
356                                 m_callerClassName,
357                                 m_callerMethodName,
358                                 m_callerMethodDesc,
359                                 m_callerMemberInfo.getModifiers(),
360                                 className,
361                                 fieldName,
362                                 fieldDesc,
363                                 fieldInfo.getModifiers(),
364                                 joinPointHash,
365                                 joinPointClassName,
366                                 m_lastLabelForLineNumber
367                         )
368                 );
369             }
370         }
371 
372         /***
373          * Returns the field info.
374          *
375          * @param classInfo
376          * @param className
377          * @param fieldName
378          * @param fieldDesc
379          * @param joinPointHash
380          * @return
381          */
382         private FieldInfo getFieldInfo(final ClassInfo classInfo,
383                                        final String className,
384                                        final String fieldName,
385                                        final String fieldDesc,
386                                        final int joinPointHash) {
387             FieldInfo fieldInfo = classInfo.getField(joinPointHash);
388             if (fieldInfo == null) {
389                 throw new Error(
390                         "field info metadata structure could not be build for field: "
391                         + className
392                         + '.'
393                         + fieldName
394                         + ':'
395                         + fieldDesc
396                 );
397             }
398             return fieldInfo;
399         }
400 
401         /***
402          * Filters out the fields that are not eligible for transformation.
403          *
404          * @param definitions
405          * @param ctx
406          * @param fieldInfo
407          * @return boolean true if the field should be filtered out
408          */
409         public boolean fieldFilter(final Set definitions,
410                                    final ExpressionContext ctx,
411                                    final FieldInfo fieldInfo) {
412             if (fieldInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
413                 return true;
414             }
415             for (Iterator it = definitions.iterator(); it.hasNext();) {
416                 if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
417                     return false;
418                 } else {
419                     continue;
420                 }
421             }
422             return true;
423         }
424     }
425 }