001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.aspectwerkz.transform.inlining.weaver;
005:
006: import com.tc.asm.ClassAdapter;
007: import com.tc.asm.ClassVisitor;
008: import com.tc.asm.Label;
009: import com.tc.asm.MethodVisitor;
010: import com.tc.asm.Type;
011:
012: import com.tc.aspectwerkz.definition.SystemDefinition;
013: import com.tc.aspectwerkz.joinpoint.management.JoinPointType;
014: import com.tc.aspectwerkz.reflect.impl.asm.AsmClassInfo;
015: import com.tc.aspectwerkz.reflect.ClassInfo;
016: import com.tc.aspectwerkz.reflect.MemberInfo;
017: import com.tc.aspectwerkz.reflect.FieldInfo;
018: import com.tc.aspectwerkz.transform.InstrumentationContext;
019: import com.tc.aspectwerkz.transform.TransformationConstants;
020: import com.tc.aspectwerkz.transform.TransformationUtil;
021: import com.tc.aspectwerkz.transform.inlining.AsmHelper;
022: import com.tc.aspectwerkz.transform.inlining.EmittedJoinPoint;
023: import com.tc.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
024: import com.tc.aspectwerkz.expression.ExpressionContext;
025: import com.tc.aspectwerkz.expression.PointcutType;
026:
027: import java.lang.reflect.Modifier;
028: import java.util.Iterator;
029: import java.util.Set;
030:
031: /**
032: * Instruments method SET and GET join points by replacing PUTFIELD and GETFIELD instructions with invocations
033: * of the compiled join point.
034: *
035: * @author <a href="mailto:jboner@codehaus.org">Jonas Bonr </a>
036: */
037: public class FieldSetFieldGetVisitor extends ClassAdapter implements
038: TransformationConstants {
039:
040: private final InstrumentationContext m_ctx;
041: private final ClassLoader m_loader;
042: private final ClassInfo m_callerClassInfo;
043:
044: private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
045:
046: /**
047: * Creates a new instance.
048: *
049: * @param cv
050: * @param loader
051: * @param classInfo
052: * @param ctx
053: */
054: public FieldSetFieldGetVisitor(final ClassVisitor cv,
055: final ClassLoader loader, final ClassInfo classInfo,
056: final InstrumentationContext ctx) {
057: super (cv);
058: m_loader = loader;
059: m_callerClassInfo = classInfo;
060: m_ctx = ctx;
061: }
062:
063: /**
064: * Visits the caller methods.
065: *
066: * @param access
067: * @param name
068: * @param desc
069: * @param signature
070: * @param exceptions
071: * @return visitor
072: */
073: public MethodVisitor visitMethod(final int access,
074: final String name, final String desc,
075: final String signature, final String[] exceptions) {
076:
077: if (name.startsWith(WRAPPER_METHOD_PREFIX)) {
078: return super .visitMethod(access, name, desc, signature,
079: exceptions);
080: }
081:
082: MethodVisitor mv = cv.visitMethod(access, name, desc,
083: signature, exceptions);
084: return mv == null ? null
085: : new ReplacePutFieldAndGetFieldInstructionCodeAdapter(
086: mv, m_loader, m_callerClassInfo, m_ctx
087: .getClassName(), name, desc);
088: }
089:
090: /**
091: * Replaces PUTFIELD and GETFIELD instructions with a call to the compiled JoinPoint instance.
092: *
093: * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
094: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
095: */
096: public class ReplacePutFieldAndGetFieldInstructionCodeAdapter
097: extends AfterObjectInitializationCodeAdapter {
098:
099: private final ClassLoader m_loader;
100: private final String m_callerClassName;
101: private final String m_callerMethodName;
102: private final String m_callerMethodDesc;
103: private final MemberInfo m_callerMemberInfo;
104:
105: /**
106: * Creates a new instance.
107: *
108: * @param ca
109: * @param loader
110: * @param callerClassInfo
111: * @param callerClassName
112: * @param callerMethodName
113: * @param callerMethodDesc
114: */
115: public ReplacePutFieldAndGetFieldInstructionCodeAdapter(
116: final MethodVisitor ca, final ClassLoader loader,
117: final ClassInfo callerClassInfo,
118: final String callerClassName,
119: final String callerMethodName,
120: final String callerMethodDesc) {
121: super (ca, callerMethodName);
122: m_loader = loader;
123: m_callerClassName = callerClassName;
124: m_callerMethodName = callerMethodName;
125: m_callerMethodDesc = callerMethodDesc;
126:
127: if (CLINIT_METHOD_NAME.equals(m_callerMethodName)) {
128: m_callerMemberInfo = callerClassInfo
129: .staticInitializer();
130: } else if (INIT_METHOD_NAME.equals(m_callerMethodName)) {
131: int hash = AsmHelper
132: .calculateConstructorHash(m_callerMethodDesc);
133: m_callerMemberInfo = callerClassInfo
134: .getConstructor(hash);
135: } else {
136: int hash = AsmHelper.calculateMethodHash(
137: m_callerMethodName, m_callerMethodDesc);
138: m_callerMemberInfo = callerClassInfo.getMethod(hash);
139: }
140: if (m_callerMemberInfo == null) {
141: System.err
142: .println("AW::WARNING "
143: + "metadata structure could not be build for method ["
144: + callerClassInfo.getName().replace(
145: '/', '.') + '.'
146: + m_callerMethodName + ':'
147: + m_callerMethodDesc + ']');
148: }
149: }
150:
151: /**
152: * Label
153: *
154: * @param label
155: */
156: public void visitLabel(Label label) {
157: m_lastLabelForLineNumber = label;
158: super .visitLabel(label);
159: }
160:
161: /**
162: * Visits PUTFIELD and GETFIELD instructions.
163: *
164: * @param opcode
165: * @param className
166: * @param fieldName
167: * @param fieldDesc
168: */
169: public void visitFieldInsn(final int opcode,
170: final String className, final String fieldName,
171: final String fieldDesc) {
172:
173: if (className
174: .endsWith(AbstractJoinPointCompiler.JOIN_POINT_CLASS_SUFFIX)
175: || fieldName.startsWith(ASPECTWERKZ_PREFIX)
176: || fieldName.startsWith(SYNTHETIC_MEMBER_PREFIX) || // synthetic field
177: fieldName.equals(SERIAL_VERSION_UID_FIELD_NAME) // can have been added by the weaver (not safe)
178: ) {
179: super .visitFieldInsn(opcode, className, fieldName,
180: fieldDesc);
181: return;
182: }
183:
184: // if within ctor, make sure object initialization has been reached
185: if (!m_isObjectInitialized) {
186: super .visitFieldInsn(opcode, className, fieldName,
187: fieldDesc);
188: return;
189: }
190:
191: final Type fieldType = Type.getType(fieldDesc);
192: final int joinPointHash = AsmHelper.calculateFieldHash(
193: fieldName, fieldDesc);
194: final ClassInfo classInfo = AsmClassInfo.getClassInfo(
195: className, m_loader);
196: final FieldInfo fieldInfo = getFieldInfo(classInfo,
197: className, fieldName, fieldDesc, joinPointHash);
198:
199: if (opcode == PUTFIELD || opcode == PUTSTATIC) {
200: handleFieldModification(fieldInfo, opcode, className,
201: fieldName, fieldDesc, joinPointHash);
202: } else if (opcode == GETFIELD || opcode == GETSTATIC) {
203: handleFieldAccess(fieldInfo, opcode, className,
204: fieldName, fieldDesc, joinPointHash, fieldType);
205: } else {
206: super .visitFieldInsn(opcode, className, fieldName,
207: fieldDesc);
208: }
209: }
210:
211: /**
212: * Handles field access.
213: *
214: * @param fieldInfo
215: * @param opcode
216: * @param className
217: * @param fieldName
218: * @param fieldDesc
219: * @param joinPointHash
220: * @param fieldType
221: */
222: private void handleFieldAccess(final FieldInfo fieldInfo,
223: final int opcode, final String className,
224: final String fieldName, final String fieldDesc,
225: int joinPointHash, final Type fieldType) {
226: if (m_callerMemberInfo == null) {
227: super .visitFieldInsn(opcode, className, fieldName,
228: fieldDesc);
229: return;
230: }
231:
232: ExpressionContext ctx = new ExpressionContext(
233: PointcutType.GET, fieldInfo, m_callerMemberInfo);
234:
235: if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
236: super .visitFieldInsn(opcode, className, fieldName,
237: fieldDesc);
238: } else {
239: m_ctx.markAsAdvised();
240:
241: String joinPointClassName = TransformationUtil
242: .getJoinPointClassName(m_callerClassName,
243: m_callerMethodName, m_callerMethodDesc,
244: className, JoinPointType.FIELD_GET_INT,
245: joinPointHash);
246:
247: // no param to field, so pass a default value to the invoke method
248: AsmHelper.addDefaultValue(this , fieldType);
249:
250: // if static context load NULL else 'this'
251: if (Modifier
252: .isStatic(m_callerMemberInfo.getModifiers())) {
253: visitInsn(ACONST_NULL);
254: } else {
255: visitVarInsn(ALOAD, 0);
256: }
257:
258: // add the call to the join point
259: super .visitMethodInsn(INVOKESTATIC, joinPointClassName,
260: INVOKE_METHOD_NAME, TransformationUtil
261: .getInvokeSignatureForFieldJoinPoints(
262: fieldInfo.getModifiers(),
263: fieldDesc, m_callerClassName,
264: className));
265:
266: // emit the joinpoint
267: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
268: JoinPointType.FIELD_GET_INT, m_callerClassName,
269: m_callerMethodName, m_callerMethodDesc,
270: m_callerMemberInfo.getModifiers(), className,
271: fieldName, fieldDesc, fieldInfo.getModifiers(),
272: joinPointHash, joinPointClassName,
273: m_lastLabelForLineNumber));
274: }
275: }
276:
277: /**
278: * Handles field modification.
279: *
280: * @param fieldInfo
281: * @param opcode
282: * @param className
283: * @param fieldName
284: * @param fieldDesc
285: * @param joinPointHash
286: */
287: private void handleFieldModification(final FieldInfo fieldInfo,
288: final int opcode, final String className,
289: final String fieldName, final String fieldDesc,
290: final int joinPointHash) {
291: if (m_callerMemberInfo == null) {
292: super .visitFieldInsn(opcode, className, fieldName,
293: fieldDesc);
294: return;
295: }
296:
297: ExpressionContext ctx = new ExpressionContext(
298: PointcutType.SET, fieldInfo, m_callerMemberInfo);
299:
300: if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
301: super .visitFieldInsn(opcode, className, fieldName,
302: fieldDesc);
303: } else {
304: m_ctx.markAsAdvised();
305:
306: String joinPointClassName = TransformationUtil
307: .getJoinPointClassName(m_callerClassName,
308: m_callerMethodName, m_callerMethodDesc,
309: className, JoinPointType.FIELD_SET_INT,
310: joinPointHash);
311:
312: // load the caller instance (this), or null if in a static context
313: // note that callee instance [optional] and args are already on the stack
314: if (Modifier
315: .isStatic(m_callerMemberInfo.getModifiers())) {
316: visitInsn(ACONST_NULL);
317: } else {
318: visitVarInsn(ALOAD, 0);
319: }
320:
321: // add the call to the join point
322: super .visitMethodInsn(INVOKESTATIC, joinPointClassName,
323: INVOKE_METHOD_NAME, TransformationUtil
324: .getInvokeSignatureForFieldJoinPoints(
325: fieldInfo.getModifiers(),
326: fieldDesc, m_callerClassName,
327: className));
328:
329: final int sort = Type.getType(fieldDesc).getSort();
330: if (sort != Type.LONG && sort != Type.DOUBLE) {
331: super .visitInsn(POP);
332: } else {
333: //AW-437
334: super .visitInsn(POP2);
335: }
336:
337: // emit the joinpoint
338: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
339: JoinPointType.FIELD_SET_INT, m_callerClassName,
340: m_callerMethodName, m_callerMethodDesc,
341: m_callerMemberInfo.getModifiers(), className,
342: fieldName, fieldDesc, fieldInfo.getModifiers(),
343: joinPointHash, joinPointClassName,
344: m_lastLabelForLineNumber));
345: }
346: }
347:
348: /**
349: * Returns the field info.
350: *
351: * @param classInfo
352: * @param className
353: * @param fieldName
354: * @param fieldDesc
355: * @param joinPointHash
356: * @return the field info
357: */
358: private FieldInfo getFieldInfo(final ClassInfo classInfo,
359: final String className, final String fieldName,
360: final String fieldDesc, final int joinPointHash) {
361: FieldInfo fieldInfo = classInfo.getField(joinPointHash);
362: if (fieldInfo == null) {
363: throw new RuntimeException(
364: "field info metadata structure could not be build for field: "
365: + className + '.' + fieldName + ':'
366: + fieldDesc);
367: }
368: return fieldInfo;
369: }
370:
371: /**
372: * Filters out the fields that are not eligible for transformation.
373: *
374: * @param definitions
375: * @param ctx
376: * @param fieldInfo
377: * @return boolean true if the field should be filtered out
378: */
379: public boolean fieldFilter(final Set definitions,
380: final ExpressionContext ctx, final FieldInfo fieldInfo) {
381: if (fieldInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
382: return true;
383: }
384: for (Iterator it = definitions.iterator(); it.hasNext();) {
385: if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
386: return false;
387: } else {
388: continue;
389: }
390: }
391: return true;
392: }
393: }
394: }
|