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.aspectj;
008:
009: import org.apache.bcel.classfile.JavaClass;
010: import org.apache.bcel.classfile.Method;
011: import org.apache.bcel.classfile.Attribute;
012: import org.apache.bcel.classfile.ClassParser;
013: import org.apache.bcel.classfile.Unknown;
014: import org.apache.bcel.generic.Type;
015:
016: import java.util.*;
017: import java.io.InputStream;
018: import java.io.IOException;
019: import java.lang.reflect.Field;
020:
021: import org.aspectj.weaver.AjAttribute;
022: import org.aspectj.weaver.ISourceContext;
023: import org.aspectj.weaver.patterns.Pointcut;
024: import org.aspectj.weaver.patterns.KindedPointcut;
025: import org.aspectj.weaver.patterns.SignaturePattern;
026:
027: import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
028: import org.codehaus.aspectwerkz.exception.DefinitionException;
029: import org.codehaus.aspectwerkz.aspect.AdviceType;
030: import org.codehaus.aspectwerkz.definition.AspectDefinition;
031: import org.codehaus.aspectwerkz.definition.DefinitionParserHelper;
032: import org.codehaus.aspectwerkz.definition.AdviceDefinition;
033: import org.codehaus.aspectwerkz.reflect.ClassInfo;
034: import org.codehaus.aspectwerkz.reflect.MethodInfo;
035: import org.codehaus.aspectwerkz.transform.inlining.spi.AspectModel;
036: import org.codehaus.aspectwerkz.transform.inlining.AdviceMethodInfo;
037: import org.codehaus.aspectwerkz.transform.inlining.AspectInfo;
038: import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
039: import org.codehaus.aspectwerkz.transform.TransformationConstants;
040:
041: import org.codehaus.aspectwerkz.org.objectweb.asm.ClassWriter;
042: import org.codehaus.aspectwerkz.org.objectweb.asm.CodeVisitor;
043:
044: /**
045: * Implementation of the AspectModel interface for the AspectJ framework.
046: * <p/>
047: * Provides methods for definition of aspects and framework specific bytecode generation
048: * used by the join point compiler.
049: *
050: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
051: */
052: public class AspectJAspectModel implements AspectModel,
053: TransformationConstants {
054:
055: private static final String ASPECT_MODEL_TYPE = "aspectj";
056: private static final String ASPECTJ_AROUND_CLOSURE_CLASS_NAME = "org/aspectj/runtime/internal/AroundClosure";
057: private static final String ASPECTJ_AROUND_CLOSURE_RUN_METHOD_NAME = "run";
058: private static final String ASPECTJ_AROUND_CLOSURE_RUN_METHOD_SIGNATURE = "([Ljava/lang/Object;)Ljava/lang/Object;";
059: private static final String PREFIX_AROUND_ADVICE = "ajc$around$";
060: private static final String PREFIX_BEFORE_ADVICE = "ajc$before$";
061: private static final String PREFIX_AFTER_FINALLY_ADVICE = "ajc$after$";
062: private static final String PREFIX_AFTER_THROWING_ADVICE = "ajc$afterThrowing$";
063: private static final String PREFIX_AFTER_RETURNING_ADVICE = "ajc$afterReturning$";
064: private static final String PROCEED_SUFFIX = "proceed";
065:
066: /**
067: * Returns the aspect model type, which is an id for the the special aspect model, can be anything as long
068: * as it is unique.
069: *
070: * @return the aspect model type id
071: */
072: public String getAspectModelType() {
073: return ASPECT_MODEL_TYPE;
074: }
075:
076: /**
077: * Defines the aspect.
078: *
079: * @param classInfo
080: * @param aspectDef
081: * @param loader
082: */
083: public void defineAspect(final ClassInfo classInfo,
084: final AspectDefinition aspectDef, final ClassLoader loader) {
085:
086: JavaClass javaClass = null;
087: String classFileName = classInfo.getName().replace('.', '/')
088: + ".class";
089: try {
090: InputStream classStream = loader
091: .getResourceAsStream(classFileName);
092: ClassParser classParser = new ClassParser(classStream,
093: classFileName);
094: javaClass = classParser.parse();
095: } catch (IOException e) {
096: throw new WrappedRuntimeException(e);
097: }
098:
099: List attributes = getAspectAttributes(javaClass);
100: if (attributes.size() == 0) {
101: return; // not an AspectJ aspect
102: }
103:
104: aspectDef.setAspectModel(getAspectModelType());
105:
106: for (Iterator it = attributes.iterator(); it.hasNext();) {
107: AjAttribute attr = (AjAttribute) it.next();
108: if (attr instanceof AjAttribute.PointcutDeclarationAttribute) {
109: AjAttribute.PointcutDeclarationAttribute pcAttr = (AjAttribute.PointcutDeclarationAttribute) attr;
110: Pointcut pointcut = pcAttr.reify().getPointcut();
111: if (pointcut instanceof KindedPointcut) {
112: try {
113: Field sigField = KindedPointcut.class
114: .getDeclaredField("signature");
115: sigField.setAccessible(true);
116: SignaturePattern signature = (SignaturePattern) sigField
117: .get(pointcut);
118: DefinitionParserHelper
119: .createAndAddPointcutDefToAspectDef(
120: signature.getName().toString(),
121: pointcut.toString(), aspectDef);
122: } catch (Exception e) {
123: throw new WrappedRuntimeException(e);
124: }
125: }
126: }
127: }
128: for (Iterator iterator = getAroundAdviceInfo(javaClass)
129: .iterator(); iterator.hasNext();) {
130: AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
131: AdviceDefinition adviceDef = handleAdviceInfo(classInfo,
132: aspectDef, ajAdviceInfo);
133: aspectDef.addAroundAdviceDefinition(adviceDef);
134: }
135: for (Iterator iterator = getBeforeAdviceInfo(javaClass)
136: .iterator(); iterator.hasNext();) {
137: AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
138: AdviceDefinition adviceDef = handleAdviceInfo(classInfo,
139: aspectDef, ajAdviceInfo);
140: aspectDef.addBeforeAdviceDefinition(adviceDef);
141: }
142: for (Iterator iterator = getAfterFinallyAdviceInfo(javaClass)
143: .iterator(); iterator.hasNext();) {
144: AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
145: AdviceDefinition adviceDef = handleAdviceInfo(classInfo,
146: aspectDef, ajAdviceInfo);
147: aspectDef.addAfterAdviceDefinition(adviceDef);
148: }
149: for (Iterator iterator = getAfterReturningAdviceInfo(javaClass)
150: .iterator(); iterator.hasNext();) {
151: AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
152: AdviceDefinition adviceDef = handleAdviceInfo(classInfo,
153: aspectDef, ajAdviceInfo);
154: aspectDef.addAfterAdviceDefinition(adviceDef);
155: }
156: for (Iterator iterator = getAfterThrowingAdviceInfo(javaClass)
157: .iterator(); iterator.hasNext();) {
158: AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
159: AdviceDefinition adviceDef = handleAdviceInfo(classInfo,
160: aspectDef, ajAdviceInfo);
161: aspectDef.addAfterAdviceDefinition(adviceDef);
162: }
163: }
164:
165: /**
166: * AspectJ is not in need for reflective information, passes contextual info through args() binding etc.
167: * or handles it itself using 'thisJoinPoint'.
168: *
169: * @return true
170: */
171: public boolean requiresReflectiveInfo() {
172: return false;
173: }
174:
175: /**
176: * Returns info about the closure class, name and type (interface or class).
177: *
178: * @return the closure class info
179: */
180: public AroundClosureClassInfo getAroundClosureClassInfo() {
181: return new AspectModel.AroundClosureClassInfo(
182: ASPECTJ_AROUND_CLOSURE_CLASS_NAME, new String[] {});
183: }
184:
185: /**
186: * Creates the methods required to implement or extend to implement the closure for the specific aspect model type.
187: *
188: * @param cw
189: * @param className
190: */
191: public void createMandatoryMethods(final ClassWriter cw,
192: final String className) {
193: CodeVisitor cv = cw.visitMethod(ACC_PUBLIC,
194: ASPECTJ_AROUND_CLOSURE_RUN_METHOD_NAME,
195: ASPECTJ_AROUND_CLOSURE_RUN_METHOD_SIGNATURE,
196: new String[] { THROWABLE_CLASS_NAME }, null);
197: cv.visitVarInsn(ALOAD, 0);
198: cv.visitMethodInsn(INVOKEVIRTUAL, className,
199: PROCEED_METHOD_NAME, PROCEED_METHOD_SIGNATURE);
200: cv.visitInsn(ARETURN);
201: cv.visitMaxs(0, 0);
202: }
203:
204: /**
205: * Creates an invocation of the around closure class' constructor.
206: *
207: * @param cv
208: */
209: public void createInvocationOfAroundClosureSuperClass(
210: final CodeVisitor cv) {
211: cv.visitInsn(ACONST_NULL);
212: cv.visitMethodInsn(INVOKESPECIAL,
213: ASPECTJ_AROUND_CLOSURE_CLASS_NAME, INIT_METHOD_NAME,
214: "([Ljava/lang/Object;)V");
215: }
216:
217: /**
218: * Creates a field to host the aspectj aspect instance
219: * <p/>
220: * TODO support other aspect deployment model
221: *
222: * @param cw
223: * @param aspectInfo
224: * @param joinPointClassName
225: */
226: public void createAspectReferenceField(final ClassWriter cw,
227: final AspectInfo aspectInfo, final String joinPointClassName) {
228: AbstractJoinPointCompiler.createAspectReferenceField(cw,
229: aspectInfo);
230: }
231:
232: /**
233: * Creates instantiation of the aspectj aspect instance by invoking aspectOf().
234: * <p/>
235: * TODO support other aspectOf() types of aspect retrieval
236: *
237: * @param cv
238: * @param aspectInfo
239: * @param joinPointClassName
240: */
241: public void createAspectInstantiation(final CodeVisitor cv,
242: final AspectInfo aspectInfo, final String joinPointClassName) {
243: cv.visitMethodInsn(INVOKESTATIC, aspectInfo
244: .getAspectClassName(), "aspectOf()", "()"
245: + aspectInfo.getAspectClassSignature());
246: // no cast needed
247: cv.visitFieldInsn(PUTSTATIC, joinPointClassName, aspectInfo
248: .getAspectFieldName(), aspectInfo
249: .getAspectClassSignature());
250:
251: }
252:
253: /**
254: * Handles the arguments to the before around.
255: *
256: * @param cv
257: * @param adviceMethodInfo
258: */
259: public void createAroundAdviceArgumentHandling(
260: final CodeVisitor cv,
261: final AdviceMethodInfo adviceMethodInfo) {
262: }
263:
264: /**
265: * Handles the arguments to the before advice.
266: *
267: * @param cv
268: * @param adviceMethodInfo
269: */
270: public void createBeforeAdviceArgumentHandling(
271: final CodeVisitor cv,
272: final AdviceMethodInfo adviceMethodInfo) {
273: }
274:
275: /**
276: * Handles the arguments to the after advice.
277: *
278: * @param cv
279: * @param adviceMethodInfo
280: */
281: public void createAfterAdviceArgumentHandling(final CodeVisitor cv,
282: final AdviceMethodInfo adviceMethodInfo) {
283: final AdviceType adviceType = adviceMethodInfo.getAdviceInfo()
284: .getType();
285: final int specialArgumentIndex = adviceMethodInfo
286: .getSpecialArgumentIndex();
287: final String specialArgumentTypeName = adviceMethodInfo
288: .getSpecialArgumentTypeName();
289: if (adviceType.equals(AdviceType.AFTER_RETURNING)
290: || adviceType.equals(AdviceType.AFTER_THROWING)) {
291: cv.visitVarInsn(ALOAD, specialArgumentIndex);
292: cv.visitTypeInsn(CHECKCAST, specialArgumentTypeName);
293: }
294: }
295:
296: /**
297: * Handles the AspectJ advice info.
298: *
299: * @param classInfo
300: * @param aspectDef
301: * @param ajAdviceInfo
302: * @return the new advice definition
303: */
304: private AdviceDefinition handleAdviceInfo(
305: final ClassInfo classInfo,
306: final AspectDefinition aspectDef,
307: final AdviceInfo ajAdviceInfo) {
308:
309: MethodInfo adviceMethod = null;
310: MethodInfo[] methods = classInfo.getMethods();
311: for (int i = 0; i < methods.length; i++) {
312: MethodInfo method = methods[i];
313: if (method.getName().equals(ajAdviceInfo.adviceMethodName)) {
314: adviceMethod = method;
315: break;
316: }
317: }
318: if (adviceMethod == null) {
319: throw new Error("advice method ["
320: + ajAdviceInfo.adviceMethodName
321: + "] could not be found in class ["
322: + classInfo.getName() + "]");
323: }
324: String specialArgType = null;
325: if (ajAdviceInfo.extraParameterFlags != 0) {
326: specialArgType = ajAdviceInfo.parameterTypes[0];
327: }
328: return AdviceDefinition.newInstance(
329: createFullAspectJAdviceMethodName(ajAdviceInfo),
330: ajAdviceInfo.type, ajAdviceInfo.pointcut,
331: specialArgType, classInfo.getName(), classInfo
332: .getName(), adviceMethod, aspectDef);
333: }
334:
335: /**
336: * Creates a full AspectJ advice method name.
337: *
338: * @param ajAdviceInfo
339: * @return the method name
340: */
341: private String createFullAspectJAdviceMethodName(
342: final AdviceInfo ajAdviceInfo) {
343: StringBuffer fullAdviceMethodName = new StringBuffer();
344: fullAdviceMethodName.append(ajAdviceInfo.adviceMethodName)
345: .append('(');
346:
347: // FIXME support args() target() and this()
348: // for (int i = 0; i < ajAdviceInfo.parameterTypes.length; i++) {
349: // String type = ajAdviceInfo.parameterTypes[i];
350: // fullAdviceMethodName.append(type).append(" arg").append(i);
351: // if (i < ajAdviceInfo.parameterTypes.length - 1) {
352: // fullAdviceMethodName.append(',');
353: // }
354: // }
355: fullAdviceMethodName.append(')');
356: return fullAdviceMethodName.toString();
357: }
358:
359: /**
360: * Returns the around advice infos.
361: *
362: * @param javaClass
363: * @return
364: */
365: private List getAroundAdviceInfo(final JavaClass javaClass) {
366: List advice = new ArrayList();
367: Method[] methods = javaClass.getMethods();
368: for (int i = 0; i < methods.length; i++) {
369: Method method = methods[i];
370: String adviceName = method.getName();
371: if (adviceName.startsWith(PREFIX_AROUND_ADVICE)
372: && !adviceName.endsWith(PROCEED_SUFFIX)) {
373: advice.add(createAdviceInfo(javaClass,
374: AdviceType.AROUND, method));
375: }
376: }
377: return advice;
378: }
379:
380: /**
381: * Returns the before advice infos.
382: *
383: * @param javaClass
384: * @return
385: */
386: private List getBeforeAdviceInfo(final JavaClass javaClass) {
387: List advice = new ArrayList();
388: Method[] methods = javaClass.getMethods();
389: for (int i = 0; i < methods.length; i++) {
390: Method method = methods[i];
391: String adviceName = method.getName();
392: if (adviceName.startsWith(PREFIX_BEFORE_ADVICE)) {
393: advice.add(createAdviceInfo(javaClass,
394: AdviceType.BEFORE, method));
395: }
396: }
397: return advice;
398: }
399:
400: /**
401: * Returns the around advice infos.
402: *
403: * @param javaClass
404: * @return
405: */
406: private List getAfterFinallyAdviceInfo(final JavaClass javaClass) {
407: List advice = new ArrayList();
408: Method[] methods = javaClass.getMethods();
409: for (int i = 0; i < methods.length; i++) {
410: Method method = methods[i];
411: String adviceName = method.getName();
412: if (adviceName.startsWith(PREFIX_AFTER_FINALLY_ADVICE)) {
413: advice.add(createAdviceInfo(javaClass,
414: AdviceType.AFTER_FINALLY, method));
415: }
416: }
417: return advice;
418: }
419:
420: /**
421: * Returns the after throwing advice infos.
422: *
423: * @param javaClass
424: * @return
425: */
426: private List getAfterThrowingAdviceInfo(final JavaClass javaClass) {
427: List advice = new ArrayList();
428: Method[] methods = javaClass.getMethods();
429: for (int i = 0; i < methods.length; i++) {
430: Method method = methods[i];
431: String adviceName = method.getName();
432: if (adviceName.startsWith(PREFIX_AFTER_THROWING_ADVICE)) {
433: advice.add(createAdviceInfo(javaClass,
434: AdviceType.AFTER_THROWING, method));
435: }
436: }
437: return advice;
438: }
439:
440: /**
441: * Returns the after returning advice infos.
442: *
443: * @param javaClass
444: * @return
445: */
446: private List getAfterReturningAdviceInfo(final JavaClass javaClass) {
447: List advice = new ArrayList();
448: Method[] methods = javaClass.getMethods();
449: for (int i = 0; i < methods.length; i++) {
450: Method method = methods[i];
451: String adviceName = method.getName();
452: if (adviceName.startsWith(PREFIX_AFTER_RETURNING_ADVICE)) {
453: advice.add(createAdviceInfo(javaClass,
454: AdviceType.AFTER_RETURNING, method));
455: }
456: }
457: return advice;
458: }
459:
460: /**
461: * Creates advice info for the advice method.
462: *
463: * @param adviceType
464: * @param method
465: * @return
466: */
467: private AdviceInfo createAdviceInfo(final JavaClass javaClassfinal,
468: final AdviceType adviceType, final Method method) {
469: Attribute[] attributes = method.getAttributes();
470: List ajAttributes = readAjAttributes(attributes, null);
471: AdviceInfo adviceInfo = new AdviceInfo();
472: for (Iterator it = ajAttributes.iterator(); it.hasNext();) {
473: AjAttribute attr = (AjAttribute) it.next();
474: if (attr instanceof AjAttribute.AdviceAttribute) {
475: AjAttribute.AdviceAttribute adviceAttr = (AjAttribute.AdviceAttribute) attr;
476: adviceInfo.type = adviceType;
477: adviceInfo.aspectClassName = javaClassfinal
478: .getClassName().replace('.', '/');
479: adviceInfo.adviceMethodName = method.getName();
480: String pointcut = adviceAttr.getPointcut().toString();
481: if (pointcut.startsWith("execution(")
482: || pointcut.startsWith("call(")
483: || pointcut.startsWith("set(")
484: || pointcut.startsWith("get(")
485: || pointcut.startsWith("handler(")
486: || pointcut.startsWith("adviceexecution(")
487: || pointcut.startsWith("within(")
488: || pointcut.startsWith("withincode(")
489: || pointcut.startsWith("cflow(")
490: || pointcut.startsWith("cflowbelow(")
491: || pointcut.startsWith("if(")
492: || pointcut.startsWith("this(")
493: || pointcut.startsWith("target(")
494: || pointcut.startsWith("args(")
495: || pointcut.startsWith("initialization(")
496: || pointcut.startsWith("staticinitialization(")
497: || pointcut.startsWith("preinitialization(")) {
498: adviceInfo.pointcut = pointcut;
499: } else if (pointcut.endsWith("()")) {
500: adviceInfo.pointcut = pointcut.substring(0,
501: pointcut.length() - 2);
502: } else {
503: throw new DefinitionException("pointcuts of type ["
504: + pointcut + " are not yet supported");
505: }
506: adviceInfo.extraParameterFlags = adviceAttr
507: .getExtraParameterFlags();
508: int nrArgs = method.getArgumentTypes().length;
509: String[] parameterTypes = new String[nrArgs];
510: for (int i = 0; i < nrArgs; i++) {
511: Type type = method.getArgumentTypes()[i];
512: parameterTypes[i] = type.toString().replace('.',
513: '/');
514: }
515: adviceInfo.parameterTypes = parameterTypes;
516: }
517: }
518: return adviceInfo;
519: }
520:
521: /**
522: * Returns the aspect attributes.
523: *
524: * @param javaClass
525: * @return
526: */
527: private List getAspectAttributes(final JavaClass javaClass) {
528: return readAjAttributes(javaClass.getAttributes(), null);
529: }
530:
531: /**
532: * Reads in the AjAttributes from the bytecode.
533: *
534: * @param attrs
535: * @param context
536: * @return
537: */
538: private static List readAjAttributes(final Attribute[] attrs,
539: final ISourceContext context) {
540: List ajAttrs = new ArrayList();
541: for (int i = attrs.length - 1; i >= 0; i--) {
542: Attribute a = attrs[i];
543: if (a instanceof Unknown) {
544: Unknown u = (Unknown) a;
545: String name = u.getName();
546: if (name.startsWith(AjAttribute.AttributePrefix)) {
547: ajAttrs.add(AjAttribute.read(name, u.getBytes(),
548: context));
549: }
550: }
551: }
552: return ajAttrs;
553: }
554: }
|