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