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.Type;
010: import com.tc.asm.MethodAdapter;
011:
012: import com.tc.aspectwerkz.definition.SystemDefinition;
013: import com.tc.aspectwerkz.joinpoint.management.JoinPointType;
014: import com.tc.aspectwerkz.transform.InstrumentationContext;
015: import com.tc.aspectwerkz.transform.TransformationConstants;
016: import com.tc.aspectwerkz.transform.TransformationUtil;
017: import com.tc.aspectwerkz.transform.inlining.AsmHelper;
018: import com.tc.aspectwerkz.transform.inlining.EmittedJoinPoint;
019: import com.tc.aspectwerkz.reflect.ClassInfo;
020: import com.tc.aspectwerkz.reflect.ConstructorInfo;
021: import com.tc.aspectwerkz.expression.ExpressionContext;
022: import com.tc.aspectwerkz.expression.PointcutType;
023:
024: import java.util.Iterator;
025: import java.util.Set;
026:
027: /**
028: * Handles constructor execution weaving.
029: * For each matching ctor, a static method is added with the same signature and with the extra thisClass parameter
030: * prepended to the list. Then the orginal ctor body is changed to call the JP.invoke, only after call to this / super
031: * initializers.
032: * <p/>
033: * TODO rename in ..execution..
034: *
035: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
036: */
037: public class ConstructorBodyVisitor extends ClassAdapter implements
038: TransformationConstants {
039:
040: private final InstrumentationContext m_ctx;
041: private final ClassInfo m_calleeClassInfo;
042: private String m_declaringTypeName;
043: private Set m_addedMethods;
044:
045: /**
046: * Creates a new instance.
047: *
048: * @param cv
049: * @param classInfo
050: * @param ctx
051: * @param addedMethods
052: */
053: public ConstructorBodyVisitor(final ClassVisitor cv,
054: final ClassInfo classInfo,
055: final InstrumentationContext ctx, final Set addedMethods) {
056: super (cv);
057: m_calleeClassInfo = classInfo;
058: m_ctx = (InstrumentationContext) ctx;
059: m_addedMethods = addedMethods;
060: }
061:
062: /**
063: * Visits the class.
064: *
065: * @param access
066: * @param name
067: * @param signature
068: * @param superName
069: * @param interfaces
070: */
071: public void visit(final int version, final int access,
072: final String name, final String signature,
073: final String super Name, final String[] interfaces) {
074: m_declaringTypeName = name;
075: super .visit(version, access, name, signature, super Name,
076: interfaces);
077: }
078:
079: /**
080: * @param access
081: * @param name
082: * @param desc
083: * @param signature
084: * @param exceptions
085: * @return
086: */
087: public MethodVisitor visitMethod(int access, String name,
088: String desc, String signature, String[] exceptions) {
089: if (!INIT_METHOD_NAME.equals(name)) {
090: return super .visitMethod(access, name, desc, signature,
091: exceptions);
092: }
093:
094: int hash = AsmHelper.calculateConstructorHash(desc);
095: ConstructorInfo constructorInfo = m_calleeClassInfo
096: .getConstructor(hash);
097: if (constructorInfo == null) {
098: System.err
099: .println("AW::WARNING "
100: + "metadata structure could not be build for constructor ["
101: + m_calleeClassInfo.getName().replace('/',
102: '.') + ".<init>: " + desc + ']');
103: return cv.visitMethod(access, name, desc, signature,
104: exceptions);
105: }
106:
107: ExpressionContext ctx = new ExpressionContext(
108: PointcutType.EXECUTION, constructorInfo,
109: constructorInfo);
110:
111: if (constructorFilter(m_ctx.getDefinitions(), ctx)) {
112: return cv.visitMethod(access, name, desc, signature,
113: exceptions);
114: } else {
115: String wrapperName = TransformationUtil
116: .getConstructorBodyMethodName(m_declaringTypeName);
117: String wrapperDesc = TransformationUtil
118: .getConstructorBodyMethodSignature(desc,
119: m_declaringTypeName);
120: if (m_addedMethods.contains(AlreadyAddedMethodAdapter
121: .getMethodKey(wrapperName, wrapperDesc))) {
122: return cv.visitMethod(access, name, desc, signature,
123: exceptions);
124: }
125:
126: m_ctx.markAsAdvised();
127:
128: // create the proxy constructor for the original constructor
129: MethodVisitor proxyCtorMethodVisitor = cv.visitMethod(
130: access, name, desc, signature, exceptions);
131: // create the ctorBodyMethod for the original constructor body
132: int modifiers = ACC_SYNTHETIC | ACC_STATIC;
133: MethodVisitor ctorBodyMethodMethodVisitor = cv.visitMethod(
134: modifiers, wrapperName, wrapperDesc, signature,
135: exceptions);
136:
137: // return a dispatch Code Adapter in between the orginal one and both of them
138: return new DispatchCtorBodyCodeAdapter(
139: proxyCtorMethodVisitor,
140: ctorBodyMethodMethodVisitor, access, desc);
141: }
142: }
143:
144: /**
145: * Creates the "proxy constructor" join point invocation body
146: *
147: * @param ctorProxy
148: * @param access
149: * @param desc
150: */
151: private void insertJoinPointInvoke(final MethodVisitor ctorProxy,
152: final int access, final String desc) {
153: // load "this"
154: ctorProxy.visitVarInsn(ALOAD, 0);// is too simple f.e. when DUP was used
155: // load args
156: AsmHelper.loadArgumentTypes(ctorProxy, Type
157: .getArgumentTypes(desc), false);
158:
159: // caller = callee
160: ctorProxy.visitVarInsn(ALOAD, 0);
161:
162: int joinPointHash = AsmHelper.calculateConstructorHash(desc);
163: String joinPointClassName = TransformationUtil
164: .getJoinPointClassName(m_declaringTypeName,
165: INIT_METHOD_NAME, desc, m_declaringTypeName,
166: JoinPointType.CONSTRUCTOR_EXECUTION_INT,
167: joinPointHash);
168:
169: ctorProxy.visitMethodInsn(INVOKESTATIC, joinPointClassName,
170: INVOKE_METHOD_NAME, TransformationUtil
171: .getInvokeSignatureForCodeJoinPoints(access,
172: desc, m_declaringTypeName,
173: m_declaringTypeName));
174:
175: // emit the joinpoint
176: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
177: JoinPointType.CONSTRUCTOR_EXECUTION_INT,
178: m_declaringTypeName,
179: TransformationConstants.INIT_METHOD_NAME, desc, access,
180: m_declaringTypeName,
181: TransformationConstants.INIT_METHOD_NAME, desc, access,
182: joinPointHash, joinPointClassName,
183: EmittedJoinPoint.NO_LINE_NUMBER));
184: }
185:
186: /**
187: * Filters out the constructor that are not eligible for transformation.
188: *
189: * @param definitions
190: * @param ctx
191: * @return boolean true if the constructor should be filtered out
192: */
193: public static boolean constructorFilter(final Set definitions,
194: final ExpressionContext ctx) {
195: for (Iterator it = definitions.iterator(); it.hasNext();) {
196: if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
197: return false;
198: } else {
199: continue;
200: }
201: }
202: return true;
203: }
204:
205: /**
206: * A class that dispatch the ctor body instruction to any other given code visitor
207: * </p>
208: * The behavior is like this:
209: * 1/ as long as the INVOKESPECIAL for the object initialization has not been reached, every bytecode
210: * instruction is dispatched in the ctor code visitor. [note 1]
211: * 2/ when this one is reached, it is only added in the ctor code visitor and a JP invoke is added
212: * 3/ after that, only the other code visitor receives the instructions
213: * </p>
214: * [note 1] To support schemes like http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#9839
215: * where the stack is like ALOAD_0 + DUP, we handle a special case.
216: * f.e. CGlib proxy ctor are like that..
217: * Don't know if some other info can be left on the stack (f.e. ILOAD 1, DUP ...)
218: */
219: private class DispatchCtorBodyCodeAdapter extends MethodAdapter {
220: private MethodVisitor m_ctorBodyMethodMethodVisitor;
221: private MethodVisitor m_proxyCtorMethodVisitor;
222: private final int m_constructorAccess;
223: private final String m_constructorDesc;
224:
225: private int m_newCount = 0;
226:
227: private boolean m_proxyCtorCodeDone = false;
228: private boolean m_isALOADDUPHeuristic = false;
229: private int m_index = -1;
230:
231: public DispatchCtorBodyCodeAdapter(MethodVisitor proxyCtor,
232: MethodVisitor ctorBodyMethod, final int access,
233: final String desc) {
234: super (proxyCtor);
235: m_proxyCtorMethodVisitor = proxyCtor;
236: m_ctorBodyMethodMethodVisitor = ctorBodyMethod;
237: m_constructorAccess = access;
238: m_constructorDesc = desc;
239: }
240:
241: public void visitInsn(int opcode) {
242: super .visitInsn(opcode);
243: if (!m_proxyCtorCodeDone && opcode == DUP && m_index == 0) {
244: // heuristic for ALOAD_0 + DUP confirmed
245: m_isALOADDUPHeuristic = true;
246: m_index++;
247: }
248: }
249:
250: public void visitIntInsn(int i, int i1) {
251: super .visitIntInsn(i, i1);
252: }
253:
254: public void visitVarInsn(int opcode, int i1) {
255: super .visitVarInsn(opcode, i1);
256: if (!m_proxyCtorCodeDone) {
257: if (opcode == ALOAD && i1 == 0) {
258: m_index++;
259: }
260: }
261: }
262:
263: public void visitFieldInsn(int i, String s, String s1, String s2) {
264: super .visitFieldInsn(i, s, s1, s2);
265: }
266:
267: public void visitLdcInsn(Object o) {
268: super .visitLdcInsn(o);
269: }
270:
271: public void visitIincInsn(int i, int i1) {
272: super .visitIincInsn(i, i1);
273: }
274:
275: public void visitMultiANewArrayInsn(String s, int i) {
276: super .visitMultiANewArrayInsn(s, i);
277: }
278:
279: /**
280: * Visit NEW type to ignore corresponding INVOKESPECIAL for those
281: *
282: * @param opcode
283: * @param name
284: */
285: public void visitTypeInsn(int opcode, String name) {
286: super .visitTypeInsn(opcode, name);
287: if (opcode == NEW) {
288: m_newCount++;
289: }
290: }
291:
292: public void visitMethodInsn(int opcode, String owner,
293: String name, String desc) {
294: if (!m_proxyCtorCodeDone) {
295: if (opcode == INVOKESPECIAL) {
296: if (m_newCount == 0) {
297: // first INVOKESPECIAL encountered to <init> for a NON new XXX()
298: m_proxyCtorMethodVisitor.visitMethodInsn(
299: opcode, owner, name, desc);
300: // insert the JoinPoint invocation
301: insertJoinPointInvoke(m_proxyCtorMethodVisitor,
302: m_constructorAccess, m_constructorDesc);
303: m_proxyCtorMethodVisitor.visitInsn(RETURN);
304: m_proxyCtorMethodVisitor.visitMaxs(0, 0);
305: m_proxyCtorMethodVisitor = null;
306: m_proxyCtorCodeDone = true;
307: mv = m_ctorBodyMethodMethodVisitor;
308: // load ALOAD 0 if under heuristic
309: if (m_isALOADDUPHeuristic) {
310: m_ctorBodyMethodMethodVisitor.visitVarInsn(
311: ALOAD, 0);
312: }
313: } else {
314: m_newCount--;
315: mv.visitMethodInsn(opcode, owner, name, desc);
316: }
317: } else {
318: mv.visitMethodInsn(opcode, owner, name, desc);
319: }
320: } else {
321: mv.visitMethodInsn(opcode, owner, name, desc);
322: }
323: }
324:
325: }
326: }
|