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