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) ||
183 fieldName.equals(SERIAL_VERSION_UID_FIELD_NAME)
184 ) {
185 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
186 return;
187 }
188
189
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
250 AsmHelper.addDefaultValue(this, fieldType);
251
252
253 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
254 visitInsn(ACONST_NULL);
255 } else {
256 visitVarInsn(ALOAD, 0);
257 }
258
259
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
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
327
328 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
329 visitInsn(ACONST_NULL);
330 } else {
331 visitVarInsn(ALOAD, 0);
332 }
333
334
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
349 super.visitInsn(POP2);
350 }
351
352
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 }