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 org.objectweb.asm.ClassAdapter;
010: import org.objectweb.asm.ClassVisitor;
011: import org.objectweb.asm.CodeVisitor;
012: import org.objectweb.asm.Attribute;
013: import org.objectweb.asm.CodeAdapter;
014: import org.objectweb.asm.Label;
015: import org.codehaus.aspectwerkz.definition.SystemDefinition;
016: import org.codehaus.aspectwerkz.expression.ExpressionContext;
017: import org.codehaus.aspectwerkz.expression.PointcutType;
018: import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
019: import org.codehaus.aspectwerkz.reflect.ClassInfo;
020: import org.codehaus.aspectwerkz.reflect.MethodInfo;
021: import org.codehaus.aspectwerkz.reflect.MemberInfo;
022: import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
023: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
024: import org.codehaus.aspectwerkz.transform.Context;
025: import org.codehaus.aspectwerkz.transform.TransformationUtil;
026: import org.codehaus.aspectwerkz.transform.TransformationConstants;
027: import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
028: import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
029: import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
030: import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
031:
032: import java.lang.reflect.Modifier;
033: import java.util.Iterator;
034: import java.util.Set;
035:
036: /**
037: * Instruments method CALL join points by replacing INVOKEXXX instructions with invocations of the compiled join point.
038: * <br/>
039: * It calls the JPClass.invoke static method. The signature of the invoke method depends if the
040: * target method is static or not as follow:
041: * <pre>
042: * invoke(callee, args.., caller) // non static
043: * invoke(args.., caller) // static
044: * </pre>
045: * (The reason why is that it simplifies call pointcut stack management)
046: *
047: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
048: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
049: */
050: public class MethodCallVisitor extends ClassAdapter implements
051: TransformationConstants {
052:
053: private final ContextImpl m_ctx;
054: private final ClassLoader m_loader;
055: private final ClassInfo m_callerClassInfo;
056:
057: private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
058:
059: /**
060: * Creates a new instance.
061: *
062: * @param cv
063: * @param loader
064: * @param classInfo
065: * @param ctx
066: */
067: public MethodCallVisitor(final ClassVisitor cv,
068: final ClassLoader loader, final ClassInfo classInfo,
069: final Context ctx) {
070: super (cv);
071: m_loader = loader;
072: m_callerClassInfo = classInfo;
073: m_ctx = (ContextImpl) ctx;
074: }
075:
076: /**
077: * Visits the caller methods.
078: *
079: * @param access
080: * @param name
081: * @param desc
082: * @param exceptions
083: * @param attrs
084: * @return
085: */
086: public CodeVisitor visitMethod(final int access, final String name,
087: final String desc, final String[] exceptions,
088: final Attribute attrs) {
089:
090: if (name.startsWith(WRAPPER_METHOD_PREFIX)
091: || Modifier.isNative(access)
092: || Modifier.isAbstract(access)) {
093: return super .visitMethod(access, name, desc, exceptions,
094: attrs);
095: }
096:
097: CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions,
098: attrs);
099: return mv == null ? null
100: : new ReplaceInvokeInstructionCodeAdapter(mv, m_loader,
101: m_callerClassInfo, m_ctx.getClassName(), name,
102: desc);
103: }
104:
105: /**
106: * Replaces 'INVOKEXXX' instructions with a call to the compiled JoinPoint instance.
107: *
108: * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
109: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
110: */
111: public class ReplaceInvokeInstructionCodeAdapter extends
112: AfterObjectInitializationCodeAdapter {
113:
114: private final ClassLoader m_loader;
115: private final ClassInfo m_callerClassInfo;
116: private final String m_callerClassName;
117: private final String m_callerMethodName;
118: private final String m_callerMethodDesc;
119: private final MemberInfo m_callerMemberInfo;
120:
121: /**
122: * Creates a new instance.
123: *
124: * @param ca
125: * @param loader
126: * @param callerClassInfo
127: * @param callerClassName
128: * @param callerMethodName
129: * @param callerMethodDesc
130: */
131: public ReplaceInvokeInstructionCodeAdapter(
132: final CodeVisitor ca, final ClassLoader loader,
133: final ClassInfo callerClassInfo,
134: final String callerClassName,
135: final String callerMethodName,
136: final String callerMethodDesc) {
137: super (ca, callerMethodName);
138: m_loader = loader;
139: m_callerClassInfo = callerClassInfo;
140: m_callerClassName = callerClassName;
141: m_callerMethodName = callerMethodName;
142: m_callerMethodDesc = callerMethodDesc;
143:
144: if (CLINIT_METHOD_NAME.equals(callerMethodName)) {
145: m_callerMemberInfo = m_callerClassInfo
146: .staticInitializer();
147: } else if (INIT_METHOD_NAME.equals(callerMethodName)) {
148: int hash = AsmHelper
149: .calculateConstructorHash(m_callerMethodDesc);
150: m_callerMemberInfo = m_callerClassInfo
151: .getConstructor(hash);
152: } else {
153: int hash = AsmHelper.calculateMethodHash(
154: m_callerMethodName, m_callerMethodDesc);
155: m_callerMemberInfo = m_callerClassInfo.getMethod(hash);
156: }
157: }
158:
159: /**
160: * Label
161: *
162: * @param label
163: */
164: public void visitLabel(Label label) {
165: m_lastLabelForLineNumber = label;
166: super .visitLabel(label);
167: }
168:
169: /**
170: * Visits 'INVOKEXXX' instructions.
171: *
172: * @param opcode
173: * @param calleeClassName
174: * @param calleeMethodName
175: * @param calleeMethodDesc
176: */
177: public void visitMethodInsn(final int opcode,
178: String calleeClassName, final String calleeMethodName,
179: final String calleeMethodDesc) {
180:
181: if (m_callerMemberInfo == null) {
182: System.err
183: .println("AW::WARNING "
184: + "metadata structure could not be build for method ["
185: + m_callerClassInfo.getName().replace(
186: '/', '.') + '.'
187: + m_callerMethodName + ':'
188: + m_callerMethodDesc + ']');
189: super .visitMethodInsn(opcode, calleeClassName,
190: calleeMethodName, calleeMethodDesc);
191: return;
192: }
193:
194: if (INIT_METHOD_NAME.equals(calleeMethodName)
195: || CLINIT_METHOD_NAME.equals(calleeMethodName)
196: || calleeMethodName.startsWith(ASPECTWERKZ_PREFIX)
197: || calleeClassName
198: .endsWith(JOIN_POINT_CLASS_SUFFIX)
199: //calleeClassName.startsWith("org/aopalliance/")) { // FIXME make generic fix by invoking all AspectModels (same problem in other visitors as well)
200: ) {
201: super .visitMethodInsn(opcode, calleeClassName,
202: calleeMethodName, calleeMethodDesc);
203: return;
204: }
205:
206: // check if we have a super.sameMethod() call
207: if (opcode == INVOKESPECIAL
208: && !calleeClassName.equals(m_callerClassName)
209: && ClassInfoHelper.extendsSuperClass(
210: m_callerClassInfo, calleeClassName.replace(
211: '/', '.'))) {
212: super .visitMethodInsn(opcode, calleeClassName,
213: calleeMethodName, calleeMethodDesc);
214: return;
215: }
216:
217: // check if object initialization has been reached
218: if (!m_isObjectInitialized) {
219: super .visitMethodInsn(opcode, calleeClassName,
220: calleeMethodName, calleeMethodDesc);
221: return;
222: }
223:
224: int joinPointHash = AsmHelper.calculateMethodHash(
225: calleeMethodName, calleeMethodDesc);
226:
227: ClassInfo classInfo = AsmClassInfo.getClassInfo(
228: calleeClassName, m_loader);
229: MethodInfo calleeMethodInfo = classInfo
230: .getMethod(joinPointHash);
231:
232: if (calleeMethodInfo == null) {
233: System.err
234: .println("AW::WARNING "
235: + "metadata structure could not be build for method ["
236: + classInfo.getName().replace('/', '.')
237: + '.' + calleeMethodName + ':'
238: + calleeMethodDesc
239: + "] when parsing method ["
240: + m_callerClassInfo.getName() + '.'
241: + m_callerMethodName + "(..)]");
242: // bail out
243: super .visitMethodInsn(opcode, calleeClassName,
244: calleeMethodName, calleeMethodDesc);
245: return;
246: }
247:
248: ExpressionContext ctx = new ExpressionContext(
249: PointcutType.CALL, calleeMethodInfo,
250: m_callerMemberInfo);
251:
252: if (methodFilter(m_ctx.getDefinitions(), ctx,
253: calleeMethodInfo)) {
254: super .visitMethodInsn(opcode, calleeClassName,
255: calleeMethodName, calleeMethodDesc);
256: } else {
257: m_ctx.markAsAdvised();
258:
259: String joinPointClassName = TransformationUtil
260: .getJoinPointClassName(m_callerClassName,
261: m_callerMethodName, m_callerMethodDesc,
262: calleeClassName,
263: JoinPointType.METHOD_CALL_INT,
264: joinPointHash);
265:
266: // load the caller instance (this), or null if in a static context
267: // note that callee instance [optional] and args are already on the stack
268: if (Modifier
269: .isStatic(m_callerMemberInfo.getModifiers())) {
270: visitInsn(ACONST_NULL);
271: } else {
272: visitVarInsn(ALOAD, 0);
273: }
274:
275: // add the call to the join point
276: super
277: .visitMethodInsn(
278: INVOKESTATIC,
279: joinPointClassName,
280: INVOKE_METHOD_NAME,
281: TransformationUtil
282: .getInvokeSignatureForCodeJoinPoints(
283: calleeMethodInfo
284: .getModifiers(),
285: calleeMethodDesc,
286: m_callerClassName,
287: calleeClassName));
288:
289: // emit the joinpoint
290: //See AW-253 - we remember if we had an INVOKE INTERFACE opcode
291: int modifiers = calleeMethodInfo.getModifiers();
292: if (opcode == INVOKEINTERFACE) {
293: modifiers = modifiers | MODIFIER_INVOKEINTERFACE;
294: }
295: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
296: JoinPointType.METHOD_CALL_INT,
297: m_callerClassName, m_callerMethodName,
298: m_callerMethodDesc, m_callerMemberInfo
299: .getModifiers(), calleeClassName,
300: calleeMethodName, calleeMethodDesc, modifiers,
301: joinPointHash, joinPointClassName,
302: m_lastLabelForLineNumber));
303: }
304: }
305:
306: /**
307: * Filters out the methods that are not eligible for transformation.
308: * Do not filter on abstract callee method - needed for interface declared method call
309: * (invokeinterface instr.)
310: *
311: * @param definitions
312: * @param ctx
313: * @param calleeMethodInfo
314: * @return boolean true if the method should be filtered out
315: */
316: public boolean methodFilter(final Set definitions,
317: final ExpressionContext ctx,
318: final MethodInfo calleeMethodInfo) {
319: if (calleeMethodInfo.getName().equals(INIT_METHOD_NAME)
320: || calleeMethodInfo.getName().equals(
321: CLINIT_METHOD_NAME)
322: || calleeMethodInfo.getName().startsWith(
323: ORIGINAL_METHOD_PREFIX)) {
324: return true;
325: }
326: for (Iterator it = definitions.iterator(); it.hasNext();) {
327: if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
328: return false;
329: } else {
330: continue;
331: }
332: }
333: return true;
334: }
335: }
336: }
|