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