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 java.lang.reflect.Modifier;
007: import java.util.Iterator;
008: import java.util.Set;
009:
010: import com.tc.asm.*;
011:
012: import com.tc.aspectwerkz.definition.SystemDefinition;
013: import com.tc.aspectwerkz.intercept.AdvisableImpl;
014: import com.tc.aspectwerkz.joinpoint.management.JoinPointType;
015: import com.tc.aspectwerkz.transform.InstrumentationContext;
016: import com.tc.aspectwerkz.transform.TransformationConstants;
017: import com.tc.aspectwerkz.transform.TransformationUtil;
018: import com.tc.aspectwerkz.transform.inlining.AsmCopyAdapter;
019: import com.tc.aspectwerkz.transform.inlining.AsmHelper;
020: import com.tc.aspectwerkz.transform.inlining.EmittedJoinPoint;
021: import com.tc.aspectwerkz.reflect.ClassInfo;
022: import com.tc.aspectwerkz.reflect.MethodInfo;
023: import com.tc.aspectwerkz.expression.PointcutType;
024: import com.tc.aspectwerkz.expression.ExpressionContext;
025:
026: /**
027: * Adds a "proxy method" to the methods that matches an <tt>execution</tt> pointcut as well as prefixing the "original
028: * method".
029: * <br/>
030: * The proxy method calls the JPClass.invoke static method. The signature of the invoke method depends if the
031: * target method is static or not as follow:
032: * <pre>
033: * invoke(callee, args.., caller) // non static
034: * invoke(args.., caller) // static
035: * </pre>
036: * (The reason why is that it simplifies call pointcut stack management)
037: *
038: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
039: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
040: */
041: public class MethodExecutionVisitor extends ClassAdapter implements
042: TransformationConstants {
043:
044: private final ClassInfo m_classInfo;
045: private final InstrumentationContext m_ctx;
046: private String m_declaringTypeName;
047: private final Set m_addedMethods;
048:
049: /**
050: * Creates a new class adapter.
051: *
052: * @param cv
053: * @param classInfo
054: * @param ctx
055: * @param addedMethods
056: */
057: public MethodExecutionVisitor(final ClassVisitor cv,
058: final ClassInfo classInfo,
059: final InstrumentationContext ctx, final Set addedMethods) {
060: super (cv);
061: m_classInfo = classInfo;
062: m_ctx = (InstrumentationContext) ctx;
063: m_addedMethods = addedMethods;
064: }
065:
066: /**
067: * Visits the class.
068: *
069: * @param access
070: * @param name
071: * @param signature
072: * @param superName
073: * @param interfaces
074: */
075: public void visit(final int version, final int access,
076: final String name, final String signature,
077: final String super Name, final String[] interfaces) {
078: m_declaringTypeName = name;
079: super .visit(version, access, name, signature, super Name,
080: interfaces);
081: }
082:
083: /**
084: * Visits the methods.
085: *
086: * @param access
087: * @param name
088: * @param desc
089: * @param signature
090: * @param exceptions
091: * @return
092: */
093: public MethodVisitor visitMethod(final int access,
094: final String name, final String desc,
095: final String signature, final String[] exceptions) {
096:
097: if (INIT_METHOD_NAME.equals(name)
098: || CLINIT_METHOD_NAME.equals(name)
099: || name.startsWith(ASPECTWERKZ_PREFIX)
100: || name.startsWith(SYNTHETIC_MEMBER_PREFIX)
101: || name.startsWith(WRAPPER_METHOD_PREFIX)
102: || (AdvisableImpl.ADD_ADVICE_METHOD_NAME.equals(name) && AdvisableImpl.ADD_ADVICE_METHOD_DESC
103: .equals(desc))
104: || (AdvisableImpl.REMOVE_ADVICE_METHOD_NAME
105: .equals(name) && AdvisableImpl.REMOVE_ADVICE_METHOD_DESC
106: .equals(desc))) {
107: return cv.visitMethod(access, name, desc, signature,
108: exceptions);
109: }
110:
111: int hash = AsmHelper.calculateMethodHash(name, desc);
112: MethodInfo methodInfo = m_classInfo.getMethod(hash);
113: if (methodInfo == null) {
114: System.err
115: .println("AW::WARNING "
116: + "metadata structure could not be build for method ["
117: + m_classInfo.getName().replace('/', '.')
118: + '.' + name + ':' + desc + ']');
119: // bail out
120: return cv.visitMethod(access, name, desc, signature,
121: exceptions);
122: }
123:
124: ExpressionContext ctx = new ExpressionContext(
125: PointcutType.EXECUTION, methodInfo, methodInfo);
126:
127: if (methodFilter(m_ctx.getDefinitions(), ctx, methodInfo)) {
128: return cv.visitMethod(access, name, desc, signature,
129: exceptions);
130: } else {
131: String prefixedOriginalName = TransformationUtil
132: .getPrefixedOriginalMethodName(name,
133: m_declaringTypeName);
134: if (m_addedMethods.contains(AlreadyAddedMethodAdapter
135: .getMethodKey(prefixedOriginalName, desc))) {
136: return cv.visitMethod(access, name, desc, signature,
137: exceptions);
138: }
139:
140: m_ctx.markAsAdvised();
141:
142: // create the proxy for the original method
143: final MethodVisitor proxyMethod = createProxyMethod(access,
144: name, desc, signature, exceptions, methodInfo);
145:
146: int modifiers = ACC_SYNTHETIC;
147: if (Modifier.isStatic(access)) {
148: modifiers |= ACC_STATIC;
149: }
150: // prefix the original method and make sure we copy method annotations to the proxyMethod
151: // while keeping the body for the prefixed method
152: return new MethodAdapter(cv.visitMethod(modifiers,
153: prefixedOriginalName, desc, signature, exceptions)) {
154: public AnnotationVisitor visitAnnotation(String desc,
155: boolean visible) {
156: return new AsmCopyAdapter.CopyAnnotationAdapter(
157: super .visitAnnotation(desc, visible),
158: proxyMethod.visitAnnotation(desc, visible));
159: }
160:
161: public AnnotationVisitor visitParameterAnnotation(
162: int parameter, String desc, boolean visible) {
163: return new AsmCopyAdapter.CopyAnnotationAdapter(
164: super .visitParameterAnnotation(parameter,
165: desc, visible), proxyMethod
166: .visitParameterAnnotation(
167: parameter, desc, visible));
168: }
169:
170: public void visitAttribute(Attribute attr) {
171: super .visitAttribute(attr);
172: proxyMethod.visitAttribute(attr);
173: }
174: };
175: }
176: }
177:
178: /**
179: * Creates the "proxy method", e.g. the method that has the same name and signature as the original method but a
180: * completely other implementation.
181: *
182: * @param access
183: * @param name
184: * @param desc
185: * @param signature
186: * @param exceptions
187: * @param methodInfo
188: * @return the method visitor
189: */
190: private MethodVisitor createProxyMethod(final int access,
191: final String name, final String desc,
192: final String signature, final String[] exceptions,
193: final MethodInfo methodInfo) {
194: MethodVisitor mv = cv.visitMethod(access, name, desc,
195: signature, exceptions);
196:
197: // load "this" ie callee if target method is not static
198: if (!Modifier.isStatic(access)) {
199: mv.visitVarInsn(ALOAD, 0);
200: }
201: // load args
202: AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc),
203: Modifier.isStatic(access));
204: // load "this" ie caller or null if method is static
205: if (Modifier.isStatic(access)) {
206: mv.visitInsn(ACONST_NULL);
207: } else {
208: mv.visitVarInsn(ALOAD, 0);
209: }
210:
211: int joinPointHash = AsmHelper.calculateMethodHash(name, desc);
212: String joinPointClassName = TransformationUtil
213: .getJoinPointClassName(m_declaringTypeName, name, desc,
214: m_declaringTypeName,
215: JoinPointType.METHOD_EXECUTION_INT,
216: joinPointHash);
217:
218: // TODO: should we provide some sort of option to do JITgen when weaving instead of when loading ?
219: // use case: offline full packaging and alike
220:
221: mv.visitMethodInsn(INVOKESTATIC, joinPointClassName,
222: INVOKE_METHOD_NAME, TransformationUtil
223: .getInvokeSignatureForCodeJoinPoints(access,
224: desc, m_declaringTypeName,
225: m_declaringTypeName));
226:
227: AsmHelper.addReturnStatement(mv, Type.getReturnType(desc));
228: mv.visitMaxs(0, 0);
229:
230: // emit the joinpoint
231: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
232: JoinPointType.METHOD_EXECUTION_INT,
233: m_declaringTypeName, name, desc, access,
234: m_declaringTypeName, name, desc, access, joinPointHash,
235: joinPointClassName, EmittedJoinPoint.NO_LINE_NUMBER));
236:
237: return mv;
238: }
239:
240: /**
241: * Filters out the methods that are not eligible for transformation.
242: *
243: * @param definitions
244: * @param ctx
245: * @param methodInfo
246: * @return boolean true if the method should be filtered out
247: */
248: public static boolean methodFilter(final Set definitions,
249: final ExpressionContext ctx, final MethodInfo methodInfo) {
250: if (Modifier.isAbstract(methodInfo.getModifiers())
251: || Modifier.isNative(methodInfo.getModifiers())
252: || methodInfo.getName().equals(INIT_METHOD_NAME)
253: || methodInfo.getName().equals(CLINIT_METHOD_NAME)
254: || methodInfo.getName().startsWith(
255: ORIGINAL_METHOD_PREFIX)) {
256: return true;
257: }
258: for (Iterator it = definitions.iterator(); it.hasNext();) {
259: if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
260: return false;
261: } else {
262: continue;
263: }
264: }
265: return true;
266: }
267: }
|