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 java.util.Set;
007: import java.util.Iterator;
008: import java.util.Collection;
009:
010: import com.tc.asm.*;
011:
012: import com.tc.aspectwerkz.DeploymentModel;
013: import com.tc.aspectwerkz.expression.PointcutType;
014: import com.tc.aspectwerkz.expression.ExpressionInfo;
015: import com.tc.aspectwerkz.expression.ExpressionContext;
016: import com.tc.aspectwerkz.reflect.ClassInfo;
017: import com.tc.aspectwerkz.definition.AdviceDefinition;
018: import com.tc.aspectwerkz.definition.DeploymentScope;
019: import com.tc.aspectwerkz.definition.SystemDefinition;
020: import com.tc.aspectwerkz.perx.PerObjectAspect;
021: import com.tc.aspectwerkz.transform.InstrumentationContext;
022: import com.tc.aspectwerkz.transform.TransformationConstants;
023:
024: /**
025: * Adds an instance level aspect management to the target class.
026: *
027: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
028: * @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
029: */
030: public class InstanceLevelAspectVisitor extends ClassAdapter implements
031: TransformationConstants {
032:
033: private final InstrumentationContext m_ctx;
034: private final ClassInfo m_classInfo;
035: private boolean m_isAdvised = false;
036:
037: /**
038: * Creates a new add interface class adapter.
039: *
040: * @param cv
041: * @param classInfo
042: * @param ctx
043: */
044: public InstanceLevelAspectVisitor(final ClassVisitor cv,
045: final ClassInfo classInfo, final InstrumentationContext ctx) {
046: super (cv);
047: m_classInfo = classInfo;
048: m_ctx = (InstrumentationContext) ctx;
049: }
050:
051: /**
052: * Visits the class.
053: *
054: * @param access
055: * @param name
056: * @param signature
057: * @param superName
058: * @param interfaces
059: */
060: public void visit(final int version, final int access,
061: final String name, final String signature,
062: final String super Name, final String[] interfaces) {
063:
064: if (classFilter(m_classInfo, m_ctx.getDefinitions())) {
065: super .visit(version, access, name, signature, super Name,
066: interfaces);
067: return;
068: }
069:
070: for (int i = 0; i < interfaces.length; i++) {
071: String anInterface = interfaces[i];
072: if (anInterface
073: .equals(HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME)) {
074: super .visit(version, access, name, signature,
075: super Name, interfaces);
076: return;
077: }
078: }
079: String[] newInterfaceArray = new String[interfaces.length + 1];
080: System.arraycopy(interfaces, 0, newInterfaceArray, 0,
081: interfaces.length);
082: newInterfaceArray[interfaces.length] = HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME;
083:
084: // add the interface
085: super .visit(version, access, name, signature, super Name,
086: newInterfaceArray);
087:
088: // add the field with the aspect instance map
089: addAspectMapField();
090:
091: // add the getAspect(..) method
092: addGetAspectMethod(name);
093:
094: // add the hasAspect(...) method
095: addHasAspectMethod(name);
096:
097: addBindAspectMethod(name);
098: }
099:
100: /**
101: * Appends mixin instantiation to the clinit method and/or init method.
102: *
103: * @param access
104: * @param name
105: * @param desc
106: * @param signature
107: * @param exceptions
108: * @return
109: */
110: public MethodVisitor visitMethod(final int access,
111: final String name, final String desc,
112: final String signature, final String[] exceptions) {
113: if (m_isAdvised) {
114: if (name.equals(INIT_METHOD_NAME)) {
115: MethodVisitor mv = new AppendToInitMethodCodeAdapter(cv
116: .visitMethod(access, name, desc, signature,
117: exceptions), name);
118: mv.visitMaxs(0, 0);
119: return mv;
120: }
121: }
122: return cv
123: .visitMethod(access, name, desc, signature, exceptions);
124: }
125:
126: /**
127: * Adds the aspect map field to the target class.
128: */
129: private void addAspectMapField() {
130: super .visitField(ACC_PRIVATE + ACC_SYNTHETIC + ACC_TRANSIENT,
131: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
132: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE, null, null);
133: }
134:
135: /**
136: * Adds the getAspect(..) method to the target class.
137: *
138: * @param name the class name of the target class
139: */
140: private void addGetAspectMethod(final String name) {
141: MethodVisitor cv = super .visitMethod(
142: ACC_PUBLIC + ACC_SYNTHETIC,
143: INSTANCE_LEVEL_GETASPECT_METHOD_NAME,
144: INSTANCE_LEVEL_GETASPECT_METHOD_SIGNATURE, null, null);
145:
146: cv.visitVarInsn(ALOAD, 0);
147: cv.visitFieldInsn(GETFIELD, name,
148: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
149: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
150: //--
151: cv.visitInsn(DUP);
152: Label ifMapNonNull = new Label();
153: cv.visitJumpInsn(IFNONNULL, ifMapNonNull);
154: cv.visitInsn(ACONST_NULL);
155: cv.visitInsn(ARETURN);
156: cv.visitLabel(ifMapNonNull);
157:
158: // // if == null, field = new HashMap()
159: // Label ifFieldNullNotLabel = new Label();
160: // cv.visitJumpInsn(IFNONNULL, ifFieldNullNotLabel);
161: // cv.visitVarInsn(ALOAD, 0);
162: // cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
163: // cv.visitInsn(DUP);
164: // cv.visitMethodInsn(
165: // INVOKESPECIAL,
166: // HASH_MAP_CLASS_NAME,
167: // INIT_METHOD_NAME,
168: // NO_PARAM_RETURN_VOID_SIGNATURE
169: // );
170: // cv.visitFieldInsn(
171: // PUTFIELD,
172: // name,
173: // INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
174: // INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
175: // );
176: // cv.visitLabel(ifFieldNullNotLabel);
177: //
178: // cv.visitVarInsn(ALOAD, 0);
179: // cv.visitFieldInsn(
180: // GETFIELD,
181: // name,
182: // INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
183: // INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
184: // );
185: //
186: // cv.visitVarInsn(ALOAD, 2);//qName
187: // cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, GET_METHOD_NAME, GET_METHOD_SIGNATURE);
188: // cv.visitVarInsn(ASTORE, 4);
189: // cv.visitVarInsn(ALOAD, 4);
190: // Label ifNullNotLabel = new Label();
191: // cv.visitJumpInsn(IFNONNULL, ifNullNotLabel);
192: // cv.visitVarInsn(ALOAD, 2);//qName
193: // cv.visitVarInsn(ALOAD, 3);//containerClassName
194: // cv.visitVarInsn(ALOAD, 0);//this (perInstance)
195: // cv.visitMethodInsn(
196: // INVOKESTATIC,
197: // ASPECTS_CLASS_NAME,
198: // ASPECT_OF_METHOD_NAME,
199: // ASPECT_OF_PER_INSTANCE_METHOD_SIGNATURE
200: // );
201: // cv.visitVarInsn(ASTORE, 4);
202: //cv.visitVarInsn(ALOAD, 0);
203: //--
204: cv.visitVarInsn(ALOAD, 1);
205: cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME,
206: GET_METHOD_NAME, GET_METHOD_SIGNATURE);
207: //--
208: // cv.visitFieldInsn(
209: // GETFIELD,
210: // name,
211: // INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
212: // INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
213: // );
214: cv.visitInsn(ARETURN);
215: // cv.visitVarInsn(ALOAD, 2);
216: // cv.visitVarInsn(ALOAD, 4);
217: // cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
218: // cv.visitInsn(POP);
219: // cv.visitLabel(ifNullNotLabel);
220: // cv.visitVarInsn(ALOAD, 4);
221: // cv.visitInsn(ARETURN);
222: cv.visitMaxs(0, 0);
223:
224: m_ctx.markAsAdvised();
225: m_isAdvised = true;
226: }
227:
228: private void addHasAspectMethod(String mapFieldName) {
229: MethodVisitor cv = super .visitMethod(
230: ACC_PUBLIC + ACC_SYNTHETIC,
231: INSTANCE_LEVEL_HASASPECT_METHOD_NAME,
232: INSTANCE_LEVEL_HASASPECT_METHOD_SIGNATURE, null, null);
233:
234: cv.visitVarInsn(ALOAD, 0);
235: cv.visitFieldInsn(GETFIELD, mapFieldName,
236: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
237: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
238: cv.visitInsn(DUP);
239: Label ifMapNonNull = new Label();
240: cv.visitJumpInsn(IFNONNULL, ifMapNonNull);
241: cv.visitInsn(ICONST_0);
242: cv.visitInsn(IRETURN);
243: cv.visitLabel(ifMapNonNull);
244: cv.visitVarInsn(ALOAD, 1);
245: cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME,
246: "containsKey", "(Ljava/lang/Object;)Z");
247: //cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, GET_METHOD_NAME, GET_METHOD_SIGNATURE);
248: //
249: // Label ifNullLabel = new Label();
250: // cv.visitJumpInsn(IFNULL, ifNullLabel);
251: // cv.visitInsn(ICONST_1);
252: // cv.visitInsn(IRETURN);
253: // cv.visitLabel(ifNullLabel);
254: // cv.visitInsn(ICONST_0);
255: cv.visitInsn(IRETURN);
256: cv.visitMaxs(0, 0);
257:
258: m_ctx.markAsAdvised();
259: m_isAdvised = true;
260: }
261:
262: private void addBindAspectMethod(final String name) {
263: MethodVisitor cv = super .visitMethod(
264: ACC_PUBLIC + ACC_SYNTHETIC,
265: INSTANCE_LEVEL_BINDASPECT_METHOD_NAME,
266: INSTANCE_LEVEL_BINDASPECT_METHOD_SIGNATURE, null, null);
267:
268: cv.visitVarInsn(ALOAD, 0);
269: cv.visitFieldInsn(GETFIELD, name,
270: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
271: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
272: // if == null, field = new HashMap()
273: Label ifFieldNullNotLabel = new Label();
274: cv.visitJumpInsn(IFNONNULL, ifFieldNullNotLabel);
275: cv.visitVarInsn(ALOAD, 0);
276: cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
277: cv.visitInsn(DUP);
278: cv.visitMethodInsn(INVOKESPECIAL, HASH_MAP_CLASS_NAME,
279: INIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE);
280: cv.visitFieldInsn(PUTFIELD, name,
281: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
282: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
283: cv.visitLabel(ifFieldNullNotLabel);
284:
285: cv.visitVarInsn(ALOAD, 0);
286: cv.visitFieldInsn(GETFIELD, name,
287: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
288: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
289: cv.visitVarInsn(ALOAD, 1);
290: cv.visitVarInsn(ALOAD, 2);
291: cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME,
292: PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
293: cv.visitVarInsn(ALOAD, 2);
294: cv.visitInsn(ARETURN);
295: }
296:
297: /**
298: * Filters the classes to be transformed.
299: *
300: * @param classInfo the class to filter
301: * @param definitions a set with the definitions
302: * @return boolean true if the method should be filtered away
303: */
304: public static boolean classFilter(final ClassInfo classInfo,
305: final Set definitions) {
306: if (classInfo.isInterface()) {
307: return true;
308: }
309:
310: ExpressionContext ctx = new ExpressionContext(
311: PointcutType.WITHIN, null, classInfo);
312:
313: for (Iterator it = definitions.iterator(); it.hasNext();) {
314: SystemDefinition systemDef = (SystemDefinition) it.next();
315: String className = classInfo.getName().replace('/', '.');
316: if (systemDef.inExcludePackage(className)) {
317: return true;
318: }
319: if (!systemDef.inIncludePackage(className)) {
320: return true;
321: }
322:
323: Collection adviceDefs = systemDef.getAdviceDefinitions();
324: for (Iterator defs = adviceDefs.iterator(); defs.hasNext();) {
325: AdviceDefinition adviceDef = (AdviceDefinition) defs
326: .next();
327: ExpressionInfo expressionInfo = adviceDef
328: .getExpressionInfo();
329: if (expressionInfo == null) {
330: continue;
331: }
332: DeploymentModel deploymentModel = adviceDef
333: .getDeploymentModel();
334:
335: // match on perinstance deployed aspects
336: if (DeploymentModel.PER_INSTANCE
337: .equals(deploymentModel)) {
338: if (expressionInfo
339: .getAdvisedClassFilterExpression().match(
340: ctx)) {
341: return false;
342: }
343: }
344:
345: // match on perthis/pertarget perX X pointcuts
346: if (adviceDef.getAspectClassName().equals(
347: PerObjectAspect.PEROBJECT_ASPECT_NAME)) {
348: ExpressionInfo perXExpressionInfo = adviceDef
349: .getExpressionInfo();
350: if (perXExpressionInfo
351: .getAdvisedClassFilterExpression().match(
352: ctx)) {
353: return false;
354: }
355: }
356: }
357:
358: // match on deployment scopes, e.g. potential perinstance deployment aspects
359: Collection deploymentScopes = systemDef
360: .getDeploymentScopes();
361: for (Iterator scopes = deploymentScopes.iterator(); scopes
362: .hasNext();) {
363: DeploymentScope deploymentScope = (DeploymentScope) scopes
364: .next();
365: ExpressionInfo expression = new ExpressionInfo(
366: deploymentScope.getExpression(), systemDef
367: .getUuid());
368: if (expression.getAdvisedClassFilterExpression().match(
369: ctx)) {
370: return false;
371: }
372: }
373: }
374:
375: return true;
376: }
377:
378: /**
379: * Adds initialization of aspect map field to end of the init method.
380: *
381: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
382: */
383: private class AppendToInitMethodCodeAdapter extends
384: AfterObjectInitializationCodeAdapter {
385:
386: private boolean m_done = false;
387:
388: public AppendToInitMethodCodeAdapter(final MethodVisitor ca,
389: String callerMemberName) {
390: super (ca, callerMemberName);
391: }
392:
393: /**
394: * Inserts the init of the aspect field right after the call to super(..) of this(..).
395: *
396: * @param opcode
397: * @param owner
398: * @param name
399: * @param desc
400: */
401: public void visitMethodInsn(int opcode, String owner,
402: String name, String desc) {
403: super .visitMethodInsn(opcode, owner, name, desc);
404: if (opcode == INVOKESPECIAL && m_isObjectInitialized
405: && !m_done) {
406: m_done = true;
407:
408: // initialize aspect map field
409: mv.visitVarInsn(ALOAD, 0);
410: mv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
411: mv.visitInsn(DUP);
412: mv.visitMethodInsn(INVOKESPECIAL, HASH_MAP_CLASS_NAME,
413: INIT_METHOD_NAME,
414: NO_PARAM_RETURN_VOID_SIGNATURE);
415: mv.visitFieldInsn(PUTFIELD, m_classInfo.getName()
416: .replace('.', '/'),
417: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
418: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
419: }
420: }
421: }
422: }
|