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.*;
007:
008: import com.tc.aspectwerkz.transform.InstrumentationContext;
009: import com.tc.aspectwerkz.transform.TransformationConstants;
010: import com.tc.aspectwerkz.transform.inlining.EmittedJoinPoint;
011:
012: import java.util.Iterator;
013:
014: /**
015: * A ClassAdapter that take care of all weaved class and add the glue between the class and its JIT dependencies.
016: * <p/>
017: * Adds a 'private static final Class aw$clazz' field a 'private static void ___AW_$_AW_$initJoinPoints()' method
018: * and patches the 'clinit' method.
019: * <p/>
020: * If the class has been made advisable, we also add a ___AW_$_AW_$emittedJoinPoints fields that gets populated.
021: *
022: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
023: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
024: * TODO: for multi weaving, we could go on in adding several AW initJoinPoints_xxWeaveCount method, but then cannot be
025: * done with RW
026: */
027: public class JoinPointInitVisitor extends ClassAdapter implements
028: TransformationConstants {
029:
030: private final InstrumentationContext m_ctx;
031: private boolean m_hasClinitMethod = false;
032: private boolean m_hasInitJoinPointsMethod = false;
033: private boolean m_hasClassField = false;
034: private boolean m_hasEmittedJoinPointsField = false;
035:
036: /**
037: * Creates a new instance.
038: *
039: * @param cv
040: * @param ctx
041: */
042: public JoinPointInitVisitor(final ClassVisitor cv,
043: final InstrumentationContext ctx) {
044: super (cv);
045: m_ctx = ctx;
046: }
047:
048: /**
049: * Visits the methods. If the AW joinPointsInit method is found, remember that, it means we are in a multi-weaving
050: * scheme. Patch the 'clinit' method if already present.
051: * <p/>
052: * TODO: multi-weaving will lead to several invocation of AW initJoinPoints and several assigment of __AW_Clazz in the patched clinit which slows down a bit the load time
053: *
054: * @see org.objectweb.asm.ClassVisitor#visitMethod(int, String, String, String, String[])
055: */
056: public MethodVisitor visitMethod(final int access,
057: final String name, final String desc,
058: final String signature, final String[] exceptions) {
059:
060: if (CLINIT_METHOD_NAME.equals(name)) {
061: m_hasClinitMethod = true;
062: // at the beginning of the existing <clinit> method
063: // ___AWClazz = Class.forName("TargetClassName");
064: // ___AW_$_AW_$initJoinPoints();
065: MethodVisitor ca = new InsertBeforeClinitCodeAdapter(cv
066: .visitMethod(access, name, desc, signature,
067: exceptions));
068: ca.visitMaxs(0, 0);
069: return ca;
070:
071: } else if (INIT_JOIN_POINTS_METHOD_NAME.equals(name)) {
072: m_hasInitJoinPointsMethod = true;
073: // add the gathered JIT dependencies for multi-weaving support
074: MethodVisitor ca = new InsertBeforeInitJoinPointsCodeAdapter(
075: cv.visitMethod(access, name, desc, signature,
076: exceptions));
077: ca.visitMaxs(0, 0);
078: return ca;
079: } else {
080: return super .visitMethod(access, name, desc, signature,
081: exceptions);
082: }
083: }
084:
085: /**
086: * Remember if we have already the static class field for multi-weaving scheme.
087: *
088: * @param access
089: * @param name
090: * @param desc
091: * @param signature
092: * @param value
093: */
094: public FieldVisitor visitField(int access, String name,
095: String desc, String signature, Object value) {
096: if (TARGET_CLASS_FIELD_NAME.equals(name)) {
097: m_hasClassField = true;
098: } else if (EMITTED_JOINPOINTS_FIELD_NAME.equals(name)) {
099: m_hasEmittedJoinPointsField = true;
100: }
101: return super .visitField(access, name, desc, signature, value);
102: }
103:
104: /**
105: * Finalize the visit. Add static class field if needed, add initJoinPoints method if needed, add <clinit>if
106: * needed.
107: */
108: public void visitEnd() {
109: if (!m_ctx.isAdvised()) {
110: super .visitEnd();
111: return;
112: }
113:
114: if (!m_hasClassField && !m_ctx.isProxy()) {
115: // create field
116: // private final static Class aw$clazz = Class.forName("TargetClassName");
117: cv.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC
118: + ACC_SYNTHETIC, TARGET_CLASS_FIELD_NAME,
119: CLASS_CLASS_SIGNATURE, null, null);
120: }
121:
122: if (!m_hasEmittedJoinPointsField
123: && (m_ctx.isMadeAdvisable() || m_ctx.isProxy())) {
124: // create field
125: // private final static Class aw$emittedJoinPoints that will host an int to Object map
126: cv.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC
127: + ACC_SYNTHETIC, EMITTED_JOINPOINTS_FIELD_NAME,
128: "Ljava/util/HashMap;", null, null);
129: }
130:
131: if (!m_hasClinitMethod) {
132: MethodVisitor ca = new InsertBeforeClinitCodeAdapter(cv
133: .visitMethod(ACC_STATIC, CLINIT_METHOD_NAME,
134: NO_PARAM_RETURN_VOID_SIGNATURE, null, null));
135: ca.visitInsn(RETURN);
136: ca.visitMaxs(0, 0);
137: }
138:
139: if (!m_hasInitJoinPointsMethod) {
140: MethodVisitor mv = new InsertBeforeInitJoinPointsCodeAdapter(
141: cv.visitMethod(ACC_PRIVATE + ACC_FINAL + ACC_STATIC
142: + ACC_SYNTHETIC,
143: INIT_JOIN_POINTS_METHOD_NAME,
144: NO_PARAM_RETURN_VOID_SIGNATURE, null, null));
145: mv.visitInsn(RETURN);
146: mv.visitMaxs(0, 0);
147: }
148:
149: cv.visitEnd();
150: }
151:
152: /**
153: * Handles the method body of the <clinit>method.
154: *
155: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
156: */
157: public class InsertBeforeClinitCodeAdapter extends MethodAdapter {
158:
159: public InsertBeforeClinitCodeAdapter(MethodVisitor ca) {
160: super (ca);
161: if (!m_hasClassField && !m_ctx.isProxy()) {
162: mv.visitLdcInsn(m_ctx.getClassName().replace('/', '.'));
163: mv
164: .visitMethodInsn(INVOKESTATIC, CLASS_CLASS,
165: FOR_NAME_METHOD_NAME,
166: FOR_NAME_METHOD_SIGNATURE);
167: mv.visitFieldInsn(PUTSTATIC, m_ctx.getClassName(),
168: TARGET_CLASS_FIELD_NAME, CLASS_CLASS_SIGNATURE);
169: }
170: if (!m_hasEmittedJoinPointsField
171: && (m_ctx.isMadeAdvisable() || m_ctx.isProxy())) {
172: // aw$emittedJoinPoints = new HashMap()
173: mv.visitTypeInsn(NEW, "java/util/HashMap");
174: mv.visitInsn(DUP);
175: mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap",
176: "<init>", "()V");
177: mv.visitFieldInsn(PUTSTATIC, m_ctx.getClassName(),
178: EMITTED_JOINPOINTS_FIELD_NAME,
179: "Ljava/util/HashMap;");
180: }
181: if (!m_hasClassField) {
182: mv.visitMethodInsn(INVOKESTATIC, m_ctx.getClassName(),
183: INIT_JOIN_POINTS_METHOD_NAME,
184: NO_PARAM_RETURN_VOID_SIGNATURE);
185: }
186: }
187: }
188:
189: /**
190: * Handles the method body of the AW initJoinPoints method.
191: *
192: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
193: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
194: */
195: public class InsertBeforeInitJoinPointsCodeAdapter extends
196: MethodAdapter {
197:
198: public InsertBeforeInitJoinPointsCodeAdapter(MethodVisitor ca) {
199: super (ca);
200:
201: // loop over emitted jp and insert call to "JoinPointManager.loadJoinPoint(...)"
202: // add calls to aw$emittedJoinPoints.put(.. new EmittedJoinPoint) if needed.
203: for (Iterator iterator = m_ctx.getEmittedJoinPoints()
204: .iterator(); iterator.hasNext();) {
205:
206: EmittedJoinPoint jp = (EmittedJoinPoint) iterator
207: .next();
208:
209: // do not add the call to loadJoinPoint if we are doing proxy weaving
210: // (since eagerly loaded already)
211: if (!m_ctx.isProxy()) {
212: mv.visitLdcInsn(new Integer(jp.getJoinPointType()));
213: mv.visitFieldInsn(GETSTATIC, m_ctx.getClassName(),
214: TARGET_CLASS_FIELD_NAME,
215: CLASS_CLASS_SIGNATURE);
216: mv.visitLdcInsn(jp.getCallerMethodName());
217: mv.visitLdcInsn(jp.getCallerMethodDesc());
218: mv.visitLdcInsn(new Integer(jp
219: .getCallerMethodModifiers()));
220: mv.visitLdcInsn(jp.getCalleeClassName());
221: mv.visitLdcInsn(jp.getCalleeMemberName());
222: mv.visitLdcInsn(jp.getCalleeMemberDesc());
223: mv.visitLdcInsn(new Integer(jp
224: .getCalleeMemberModifiers()));
225: mv.visitLdcInsn(new Integer(jp.getJoinPointHash()));
226: mv.visitLdcInsn(jp.getJoinPointClassName());
227: mv.visitMethodInsn(INVOKESTATIC,
228: JOIN_POINT_MANAGER_CLASS_NAME,
229: LOAD_JOIN_POINT_METHOD_NAME,
230: LOAD_JOIN_POINT_METHOD_SIGNATURE);
231: }
232:
233: // add map with emitted JP if advisable or proxy weaving
234: if (m_ctx.isMadeAdvisable() || m_ctx.isProxy()) {
235: // emittedJoinPoints map
236: mv.visitFieldInsn(GETSTATIC, m_ctx.getClassName(),
237: EMITTED_JOINPOINTS_FIELD_NAME,
238: "Ljava/util/HashMap;");
239: // "boxed" map key
240: mv.visitTypeInsn(NEW, "java/lang/Integer");
241: mv.visitInsn(DUP);
242: mv.visitLdcInsn(new Integer(jp
243: .getJoinPointClassName().hashCode()));
244: mv.visitMethodInsn(INVOKESPECIAL,
245: "java/lang/Integer", "<init>", "(I)V");
246:
247: mv
248: .visitTypeInsn(NEW,
249: "com/tc/aspectwerkz/transform/inlining/EmittedJoinPoint");
250: mv.visitInsn(DUP);
251:
252: mv.visitLdcInsn(new Integer(jp.getJoinPointType()));
253:
254: mv.visitLdcInsn(m_ctx.getClassName());
255: mv.visitLdcInsn(jp.getCallerMethodName());
256: mv.visitLdcInsn(jp.getCallerMethodDesc());
257: mv.visitLdcInsn(new Integer(jp
258: .getCallerMethodModifiers()));
259:
260: mv.visitLdcInsn(jp.getCalleeClassName());
261: mv.visitLdcInsn(jp.getCalleeMemberName());
262: mv.visitLdcInsn(jp.getCalleeMemberDesc());
263: mv.visitLdcInsn(new Integer(jp
264: .getCalleeMemberModifiers()));
265:
266: mv.visitLdcInsn(new Integer(jp.getJoinPointHash()));
267: mv.visitLdcInsn(jp.getJoinPointClassName());
268:
269: mv
270: .visitMethodInsn(
271: INVOKESPECIAL,
272: "com/tc/aspectwerkz/transform/inlining/EmittedJoinPoint",
273: "<init>",
274: "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
275: mv.visitMethodInsn(INVOKEVIRTUAL,
276: "java/util/HashMap", "put",
277: "(ILjava/lang/Object;)Ljava/lang/Object;");
278: }
279: }
280: }
281: }
282: }
|