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 java.util.Set;
010: import java.util.Iterator;
011: import java.util.Collection;
012: import java.util.HashMap;
013:
014: import org.objectweb.asm.*;
015: import org.codehaus.aspectwerkz.transform.Context;
016: import org.codehaus.aspectwerkz.transform.TransformationConstants;
017: import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
018: import org.codehaus.aspectwerkz.reflect.ClassInfo;
019: import org.codehaus.aspectwerkz.expression.ExpressionContext;
020: import org.codehaus.aspectwerkz.expression.PointcutType;
021: import org.codehaus.aspectwerkz.expression.ExpressionInfo;
022: import org.codehaus.aspectwerkz.definition.SystemDefinition;
023: import org.codehaus.aspectwerkz.definition.AdviceDefinition;
024: import org.codehaus.aspectwerkz.definition.DeploymentScope;
025: import org.codehaus.aspectwerkz.DeploymentModel;
026:
027: /**
028: * Adds an instance level aspect management to the target class.
029: *
030: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
031: */
032: public class InstanceLevelAspectVisitor extends ClassAdapter implements
033: TransformationConstants {
034:
035: private final ContextImpl m_ctx;
036: private final ClassInfo m_classInfo;
037: private boolean m_isAdvised = false;
038:
039: /**
040: * Creates a new add interface class adapter.
041: *
042: * @param cv
043: * @param classInfo
044: * @param ctx
045: */
046: public InstanceLevelAspectVisitor(final ClassVisitor cv,
047: final ClassInfo classInfo, final Context ctx) {
048: super (cv);
049: m_classInfo = classInfo;
050: m_ctx = (ContextImpl) ctx;
051: }
052:
053: /**
054: * Visits the class.
055: *
056: * @param access
057: * @param name
058: * @param superName
059: * @param interfaces
060: * @param sourceFile
061: */
062: public void visit(final int version, final int access,
063: final String name, final String super Name,
064: final String[] interfaces, final String sourceFile) {
065:
066: if (classFilter(m_classInfo, m_ctx.getDefinitions())) {
067: super .visit(version, access, name, super Name, interfaces,
068: sourceFile);
069: return;
070: }
071:
072: for (int i = 0; i < interfaces.length; i++) {
073: String anInterface = interfaces[i];
074: if (anInterface
075: .equals(HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME)) {
076: super .visit(version, access, name, super Name,
077: interfaces, sourceFile);
078: return;
079: }
080: }
081: String[] newInterfaceArray = new String[interfaces.length + 1];
082: for (int i = 0; i < interfaces.length; i++) {
083: newInterfaceArray[i] = interfaces[i];
084: }
085: newInterfaceArray[interfaces.length] = HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME;
086:
087: // add the interface
088: super .visit(version, access, name, super Name,
089: newInterfaceArray, sourceFile);
090:
091: // add the field with the aspect instance map
092: addAspectMapField();
093:
094: // add the getAspect(..) method
095: addGetAspectMethod(name);
096: }
097:
098: /**
099: * Appends mixin instantiation to the clinit method and/or init method.
100: *
101: * @param access
102: * @param name
103: * @param desc
104: * @param exceptions
105: * @param attrs
106: * @return
107: */
108: public CodeVisitor visitMethod(final int access, final String name,
109: final String desc, final String[] exceptions,
110: final Attribute attrs) {
111: if (m_isAdvised) {
112: if (name.equals(INIT_METHOD_NAME)) {
113: CodeVisitor mv = new AppendToInitMethodCodeAdapter(cv
114: .visitMethod(access, name, desc, exceptions,
115: attrs), name);
116: mv.visitMaxs(0, 0);
117: return mv;
118: }
119: }
120: return cv.visitMethod(access, name, desc, exceptions, attrs);
121: }
122:
123: /**
124: * Adds the aspect map field to the target class.
125: */
126: private void addAspectMapField() {
127: super .visitField(ACC_PRIVATE + ACC_SYNTHETIC + ACC_TRANSIENT,
128: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
129: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE, null, null);
130: }
131:
132: /**
133: * Adds the getAspect(..) method to the target class.
134: *
135: * @param name the class name of the target class
136: */
137: private void addGetAspectMethod(final String name) {
138: CodeVisitor cv = super .visitMethod(ACC_PUBLIC + ACC_SYNTHETIC,
139: GET_INSTANCE_LEVEL_ASPECT_METHOD_NAME,
140: GET_INSTANCE_LEVEL_ASPECT_METHOD_SIGNATURE, null, null);
141:
142: cv.visitVarInsn(ALOAD, 0);
143: cv.visitFieldInsn(GETFIELD, name,
144: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
145: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
146:
147: // if == null, field = new HashMap()
148: Label ifFieldNullNotLabel = new Label();
149: cv.visitJumpInsn(IFNONNULL, ifFieldNullNotLabel);
150: cv.visitVarInsn(ALOAD, 0);
151: cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
152: cv.visitInsn(DUP);
153: cv.visitMethodInsn(INVOKESPECIAL, HASH_MAP_CLASS_NAME,
154: INIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE);
155: cv.visitFieldInsn(PUTFIELD, m_classInfo.getName().replace('.',
156: '/'), INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
157: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
158: cv.visitLabel(ifFieldNullNotLabel);
159:
160: cv.visitVarInsn(ALOAD, 0);
161: cv.visitFieldInsn(GETFIELD, name,
162: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
163: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
164:
165: cv.visitVarInsn(ALOAD, 2);//qName
166: cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME,
167: GET_METHOD_NAME, GET_METHOD_SIGNATURE);
168: cv.visitVarInsn(ASTORE, 4);
169: cv.visitVarInsn(ALOAD, 4);
170: Label ifNullNotLabel = new Label();
171: cv.visitJumpInsn(IFNONNULL, ifNullNotLabel);
172: cv.visitVarInsn(ALOAD, 2);//qName
173: cv.visitVarInsn(ALOAD, 3);//containerClassName
174: cv.visitVarInsn(ALOAD, 0);//this (perInstance)
175: cv.visitMethodInsn(INVOKESTATIC, ASPECTS_CLASS_NAME,
176: ASPECT_OF_METHOD_NAME,
177: ASPECT_OF_PER_INSTANCE_METHOD_SIGNATURE);
178: cv.visitVarInsn(ASTORE, 4);
179: cv.visitVarInsn(ALOAD, 0);
180: cv.visitFieldInsn(GETFIELD, name,
181: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
182: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
183: cv.visitVarInsn(ALOAD, 2);
184: cv.visitVarInsn(ALOAD, 4);
185: cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME,
186: PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
187: cv.visitInsn(POP);
188: cv.visitLabel(ifNullNotLabel);
189: cv.visitVarInsn(ALOAD, 4);
190: cv.visitInsn(ARETURN);
191: cv.visitMaxs(0, 0);
192:
193: m_ctx.markAsAdvised();
194: m_isAdvised = true;
195: }
196:
197: /**
198: * Filters the classes to be transformed.
199: *
200: * @param classInfo the class to filter
201: * @param definitions a set with the definitions
202: * @return boolean true if the method should be filtered away
203: */
204: public static boolean classFilter(final ClassInfo classInfo,
205: final Set definitions) {
206: if (classInfo.isInterface()) {
207: return true;
208: }
209:
210: ExpressionContext ctx = new ExpressionContext(
211: PointcutType.WITHIN, null, classInfo);
212:
213: for (Iterator it = definitions.iterator(); it.hasNext();) {
214: SystemDefinition systemDef = (SystemDefinition) it.next();
215: String className = classInfo.getName().replace('/', '.');
216: if (systemDef.inExcludePackage(className)) {
217: return true;
218: }
219: if (!systemDef.inIncludePackage(className)) {
220: return true;
221: }
222:
223: // match on perinstance deployed aspects
224: Collection adviceDefs = systemDef.getAdviceDefinitions();
225: for (Iterator defs = adviceDefs.iterator(); defs.hasNext();) {
226: AdviceDefinition adviceDef = (AdviceDefinition) defs
227: .next();
228: ExpressionInfo expressionInfo = adviceDef
229: .getExpressionInfo();
230: if (expressionInfo == null) {
231: continue;
232: }
233: if (adviceDef.getAspectDefinition()
234: .getDeploymentModel().equals(
235: DeploymentModel.PER_INSTANCE)
236: && expressionInfo
237: .getAdvisedClassFilterExpression()
238: .match(ctx)) {
239: return false;
240: }
241: }
242:
243: // match on deployment scopes, e.g. potential perinstance deployment aspects
244: Collection deploymentScopes = systemDef
245: .getDeploymentScopes();
246: for (Iterator scopes = deploymentScopes.iterator(); scopes
247: .hasNext();) {
248: DeploymentScope deploymentScope = (DeploymentScope) scopes
249: .next();
250: ExpressionInfo expression = new ExpressionInfo(
251: deploymentScope.getExpression(), systemDef
252: .getUuid());
253: if (expression.getAdvisedClassFilterExpression().match(
254: ctx)) {
255: return false;
256: }
257: }
258: }
259:
260: return true;
261: }
262:
263: /**
264: * Adds initialization of aspect map field to end of the init method.
265: *
266: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
267: */
268: private class AppendToInitMethodCodeAdapter extends
269: AfterObjectInitializationCodeAdapter {
270:
271: private boolean m_done = false;
272:
273: public AppendToInitMethodCodeAdapter(final CodeVisitor ca,
274: String callerMemberName) {
275: super (ca, callerMemberName);
276: }
277:
278: /**
279: * Inserts the init of the aspect field right after the call to super(..) of this(..).
280: *
281: * @param opcode
282: * @param owner
283: * @param name
284: * @param desc
285: */
286: public void visitMethodInsn(int opcode, String owner,
287: String name, String desc) {
288: super .visitMethodInsn(opcode, owner, name, desc);
289: if (opcode == INVOKESPECIAL && m_isObjectInitialized
290: && !m_done) {
291: m_done = true;
292:
293: // initialize aspect map field
294: cv.visitVarInsn(ALOAD, 0);
295: cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
296: cv.visitInsn(DUP);
297: cv.visitMethodInsn(INVOKESPECIAL, HASH_MAP_CLASS_NAME,
298: INIT_METHOD_NAME,
299: NO_PARAM_RETURN_VOID_SIGNATURE);
300: cv.visitFieldInsn(PUTFIELD, m_classInfo.getName()
301: .replace('.', '/'),
302: INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
303: INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE);
304: }
305: }
306: }
307: }
|