All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.weaver.loadtime.ConcreteAspectCodeGen Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2005 Contributors.
 * All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * Contributors:
 *   Alexandre Vasseur         initial implementation
 *******************************************************************************/
package org.aspectj.weaver.loadtime;

import static org.aspectj.apache.bcel.generic.Type.NO_ARGS;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue;
import org.aspectj.apache.bcel.classfile.annotation.ElementValue;
import org.aspectj.apache.bcel.classfile.annotation.NameValuePair;
import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue;
import org.aspectj.apache.bcel.generic.FieldGen;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.LocalVariableTag;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.Message;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.GeneratedReferenceTypeDelegate;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.BcelAnnotation;
import org.aspectj.weaver.bcel.BcelPerClauseAspectAdder;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.bcel.LazyClassGen;
import org.aspectj.weaver.bcel.LazyMethodGen;
import org.aspectj.weaver.loadtime.definition.Definition;
import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind;
import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind;
import org.aspectj.weaver.loadtime.definition.Definition.PointcutAndAdvice;
import org.aspectj.weaver.patterns.BasicTokenSource;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.ISignaturePattern;
import org.aspectj.weaver.patterns.ITokenSource;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.PerClause;
import org.aspectj.weaver.patterns.TypePattern;

/**
 * Generates bytecode for concrete-aspect.
 * 

* The concrete aspect is generated annotation style aspect (so traditional Java constructs annotated with our AspectJ annotations). * As it is built during aop.xml definitions registration we perform the type munging for perclause, ie. aspectOf() artifact * directly, instead of waiting for it to go thru the weaver (that we are in the middle of configuring). * * @author Alexandre Vasseur * @author Andy Clement */ public class ConcreteAspectCodeGen { private final static String[] EMPTY_STRINGS = new String[0]; /** * Concrete aspect definition we build for */ private final Definition.ConcreteAspect concreteAspect; /** * World for which we build for */ private final World world; /** * Set to true when all is checks are verified */ private boolean isValid = false; /** * The parent aspect, not concretized */ private ResolvedType parent; /** * Aspect perClause, used for direct munging of aspectOf artifacts */ private PerClause perclause; /** * Bytecode for the generated class */ private byte[] bytes; /** * Create a new generator for a concrete aspect * * @param concreteAspect the aspect definition * @param world the related world (for type resolution, etc) */ ConcreteAspectCodeGen(Definition.ConcreteAspect concreteAspect, World world) { this.concreteAspect = concreteAspect; this.world = world; } /** * Checks that concrete aspect is valid. * * @return true if ok, false otherwise */ public boolean validate() { if (!(world instanceof BcelWorld)) { reportError("Internal error: world must be of type BcelWorld"); return false; } // name must be undefined so far // TODO only convert the name to signature once, probably earlier than this ResolvedType current = world.lookupBySignature(UnresolvedType.forName(concreteAspect.name).getSignature()); if (current != null && !current.isMissing()) { reportError("Attempt to concretize but chosen aspect name already defined: " + stringify()); return false; } if (concreteAspect.pointcutsAndAdvice.size() != 0) { isValid = true; return true; } if (concreteAspect.declareAnnotations.size()!=0) { isValid = true; return true; } // it can happen that extends is null, for precedence only declaration if (concreteAspect.extend == null && concreteAspect.precedence != null) { if (concreteAspect.pointcuts.isEmpty()) { isValid = true; // m_perClause = new PerSingleton(); parent = null; return true;// no need to checks more in that special case } else { reportError("Attempt to use nested pointcuts without extends clause: " + stringify()); return false; } } String parentAspectName = concreteAspect.extend; if (parentAspectName.contains("<")) { // yikes, generic parent parent = world.resolve(UnresolvedType.forName(parentAspectName), true); if (parent.isMissing()) { reportError("Unable to resolve type reference: " + stringify()); return false; } if (parent.isParameterizedType()) { UnresolvedType[] typeParameters = parent.getTypeParameters(); for (UnresolvedType typeParameter : typeParameters) { if (typeParameter instanceof ResolvedType && ((ResolvedType) typeParameter).isMissing()) { reportError("Unablet to resolve type parameter '" + typeParameter.getName() + "' from " + stringify()); return false; } } } } else { parent = world.resolve(concreteAspect.extend, true); } // handle inner classes if (parent.isMissing()) { // fallback on inner class lookup mechanism String fixedName = concreteAspect.extend; int hasDot = fixedName.lastIndexOf('.'); while (hasDot > 0) { char[] fixedNameChars = fixedName.toCharArray(); fixedNameChars[hasDot] = '$'; fixedName = new String(fixedNameChars); hasDot = fixedName.lastIndexOf('.'); parent = world.resolve(UnresolvedType.forName(fixedName), true); if (!parent.isMissing()) { break; } } } if (parent.isMissing()) { reportError("Cannot find parent aspect for: " + stringify()); return false; } // extends must be abstract (allow for special case of Object where just using aspect for deows) if (!(parent.isAbstract() || parent.equals(ResolvedType.OBJECT))) { reportError("Attempt to concretize a non-abstract aspect: " + stringify()); return false; } // m_parent must be aspect (allow for special case of Object where just using aspect for deows) if (!(parent.isAspect() || parent.equals(ResolvedType.OBJECT))) { reportError("Attempt to concretize a non aspect: " + stringify()); return false; } // must have all abstractions defined List elligibleAbstractions = new ArrayList<>(); Collection abstractMethods = getOutstandingAbstractMethods(parent); for (ResolvedMember method : abstractMethods) { if ("()V".equals(method.getSignature())) { String n = method.getName(); // Allow for the abstract pointcut being from a code style // aspect compiled with -1.5 (see test for 128744) if (n.startsWith("ajc$pointcut")) { n = n.substring(14); n = n.substring(0, n.indexOf("$")); elligibleAbstractions.add(n); } else if (hasPointcutAnnotation(method)) { elligibleAbstractions.add(method.getName()); } else { // error, an outstanding abstract method that can't be // concretized in XML reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify()); return false; } } else { if (method.getName().startsWith("ajc$pointcut") || hasPointcutAnnotation(method)) { // it may be a pointcut but it doesn't meet the requirements for XML concretization reportError("Abstract method '" + method.toString() + "' cannot be concretized as a pointcut (illegal signature, must have no arguments, must return void): " + stringify()); return false; } else { // error, an outstanding abstract method that can't be // concretized in XML reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify()); return false; } } } List pointcutNames = new ArrayList<>(); for (Definition.Pointcut abstractPc : concreteAspect.pointcuts) { pointcutNames.add(abstractPc.name); } for (String elligiblePc : elligibleAbstractions) { if (!pointcutNames.contains(elligiblePc)) { reportError("Abstract pointcut '" + elligiblePc + "' not configured: " + stringify()); return false; } } if (concreteAspect.perclause != null) { String perclauseString = concreteAspect.perclause; if (perclauseString.startsWith("persingleton")) { } else if (perclauseString.startsWith("percflow")) { } else if (perclauseString.startsWith("pertypewithin")) { } else if (perclauseString.startsWith("perthis")) { } else if (perclauseString.startsWith("pertarget")) { } else if (perclauseString.startsWith("percflowbelow")) { } else { reportError("Unrecognized per clause specified " + stringify()); return false; } } isValid = true; return isValid; } private Collection getOutstandingAbstractMethods(ResolvedType type) { Map collector = new HashMap<>(); // let's get to the top of the hierarchy and then walk down ... // recording abstract methods then removing // them if they get defined further down the hierarchy getOutstandingAbstractMethodsHelper(type, collector); return collector.values(); } // We are trying to determine abstract methods left over at the bottom of a // hierarchy that have not been concretized. private void getOutstandingAbstractMethodsHelper(ResolvedType type, Map collector) { if (type == null) { return; } // Get to the top if (!type.equals(ResolvedType.OBJECT)) { if (type.getSuperclass() != null) { getOutstandingAbstractMethodsHelper(type.getSuperclass(), collector); } } ResolvedMember[] rms = type.getDeclaredMethods(); if (rms != null) { for (ResolvedMember member : rms) { String key = member.getName() + member.getSignature(); if (member.isAbstract()) { collector.put(key, member); } else { collector.remove(key); } } } } /** * Rebuild the XML snip that defines this concrete aspect, for log error purpose * * @return string repr. */ private String stringify() { StringBuilder sb = new StringBuilder(" in aop.xml"); // TODO needs the extra state from the definition (concretized pointcuts and advice definitions) return sb.toString(); } private boolean hasPointcutAnnotation(ResolvedMember member) { AnnotationAJ[] as = member.getAnnotations(); if (as == null || as.length == 0) { return false; } for (AnnotationAJ a : as) { if (a.getTypeSignature().equals("Lorg/aspectj/lang/annotation/Pointcut;")) { return true; } } return false; } public String getClassName() { return concreteAspect.name; } /** * @return the bytecode for the concrete aspect */ public byte[] getBytes() { if (!isValid) { throw new RuntimeException("Must validate first"); } if (bytes != null) { return bytes; } PerClause.Kind perclauseKind = PerClause.SINGLETON; PerClause parentPerClause = (parent != null ? parent.getPerClause() : null); if (parentPerClause != null) { perclauseKind = parentPerClause.getKind(); } String perclauseString = null; if (concreteAspect.perclause != null) { perclauseString = concreteAspect.perclause; if (perclauseString.startsWith("persingleton")) { perclauseKind = PerClause.SINGLETON; } else if (perclauseString.startsWith("percflow")) { perclauseKind = PerClause.PERCFLOW; } else if (perclauseString.startsWith("pertypewithin")) { perclauseKind = PerClause.PERTYPEWITHIN; } else if (perclauseString.startsWith("perthis")) { perclauseKind = PerClause.PEROBJECT; } else if (perclauseString.startsWith("pertarget")) { perclauseKind = PerClause.PEROBJECT; } else if (perclauseString.startsWith("percflowbelow")) { perclauseKind = PerClause.PERCFLOW; } } // @Aspect //inherit clause from m_parent // @DeclarePrecedence("....") // if any // public class xxxName [extends xxxExtends] { // [@Pointcut(xxxExpression-n) // public void xxxName-n() {}] // } String parentName = "java/lang/Object"; if (parent != null) { if (parent.isParameterizedType()) { parentName = parent.getGenericType().getName().replace('.', '/'); } else { parentName = parent.getName().replace('.', '/'); } } // @Aspect public class ... // TODO AV - we could point to the aop.xml that defines it and use JSR-45 LazyClassGen cg = new LazyClassGen(concreteAspect.name.replace('.', '/'), parentName, null, Modifier.PUBLIC + Constants.ACC_SUPER, EMPTY_STRINGS, world); if (parent != null && parent.isParameterizedType()) { cg.setSuperClass(parent); } if (perclauseString == null) { AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"), Collections. emptyList(), true, cg.getConstantPool()); cg.addAnnotation(ag); } else { // List elems = new ArrayList(); List elems = new ArrayList<>(); elems.add(new NameValuePair("value", new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), perclauseString), cg.getConstantPool())); AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"), elems, true, cg.getConstantPool()); cg.addAnnotation(ag); } if (concreteAspect.precedence != null) { SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), concreteAspect.precedence); List elems = new ArrayList<>(); elems.add(new NameValuePair("value", svg, cg.getConstantPool())); AnnotationGen agprec = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/DeclarePrecedence"), elems, true, cg.getConstantPool()); cg.addAnnotation(agprec); } // default constructor LazyMethodGen init = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "", NO_ARGS, EMPTY_STRINGS, cg); InstructionList cbody = init.getBody(); cbody.append(InstructionConstants.ALOAD_0); cbody.append(cg.getFactory().createInvoke(parentName, "", Type.VOID, NO_ARGS, Constants.INVOKESPECIAL)); cbody.append(InstructionConstants.RETURN); cg.addMethodGen(init); for (Definition.Pointcut abstractPc : concreteAspect.pointcuts) { // TODO AV - respect visibility instead of opening up as public? LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, abstractPc.name, NO_ARGS, EMPTY_STRINGS, cg); SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), abstractPc.expression); List elems = new ArrayList<>(); elems.add(new NameValuePair("value", svg, cg.getConstantPool())); AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Pointcut"), elems, true, cg.getConstantPool()); AnnotationAJ max = new BcelAnnotation(mag, world); mg.addAnnotation(max); InstructionList body = mg.getBody(); body.append(InstructionConstants.RETURN); cg.addMethodGen(mg); } // Construct any defined declare error/warnings if (concreteAspect.deows.size() > 0) { int counter = 1; for (Definition.DeclareErrorOrWarning deow : concreteAspect.deows) { // Building this: // @DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)") // static final String aMessage = "Only DAOs should be calling JDBC."; FieldGen field = new FieldGen(Modifier.FINAL, ObjectType.STRING, "rule" + (counter++), cg.getConstantPool()); SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), deow.pointcut); List elems = new ArrayList<>(); elems.add(new NameValuePair("value", svg, cg.getConstantPool())); AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Declare" + (deow.isError ? "Error" : "Warning")), elems, true, cg.getConstantPool()); field.addAnnotation(mag); field.setValue(deow.message); cg.addField(field, null); } } if (concreteAspect.pointcutsAndAdvice.size() > 0) { int adviceCounter = 1; for (PointcutAndAdvice paa : concreteAspect.pointcutsAndAdvice) { generateAdviceMethod(paa, adviceCounter, cg); adviceCounter++; } } if (concreteAspect.declareAnnotations.size()>0) { int decCounter = 1; for (Definition.DeclareAnnotation da: concreteAspect.declareAnnotations) { generateDeclareAnnotation(da,decCounter++,cg); } } // handle the perClause ReferenceType rt = new ReferenceType(ResolvedType.forName(concreteAspect.name).getSignature(), world); GeneratedReferenceTypeDelegate grtd = new GeneratedReferenceTypeDelegate(rt); grtd.setSuperclass(parent); rt.setDelegate(grtd); BcelPerClauseAspectAdder perClauseMunger = new BcelPerClauseAspectAdder(rt, perclauseKind); perClauseMunger.forceMunge(cg, false); // TODO AV - unsafe cast // register the fresh new class into the world repository as it does not // exist on the classpath anywhere JavaClass jc = cg.getJavaClass((BcelWorld) world); ((BcelWorld) world).addSourceObjectType(jc, true); bytes = jc.getBytes(); return bytes; } /** * The DeclareAnnotation object encapsulates an method/field/type descriptor and an annotation. This uses a DeclareAnnotation object * captured from the XML (something like '') * and builds the same construct that would have existed if the code style variant was used. This involves creating a member upon * which to hang the real annotation and then creating a classfile level attribute indicating a declare annotation is present * (that includes the signature pattern and a pointer to the real member holding the annotation). * */ private void generateDeclareAnnotation(Definition.DeclareAnnotation da, int decCounter, LazyClassGen cg) { // Here is an example member from a code style declare annotation: //void ajc$declare_at_method_1(); // RuntimeInvisibleAnnotations: length = 0x6 // 00 01 00 1B 00 00 // RuntimeVisibleAnnotations: length = 0x15 // 00 01 00 1D 00 03 00 1E 73 00 1F 00 20 73 00 21 // 00 22 73 00 23 // org.aspectj.weaver.MethodDeclarationLineNumber: length = 0x8 // 00 00 00 02 00 00 00 16 // org.aspectj.weaver.AjSynthetic: length = 0x // // Code: // Stack=0, Locals=1, Args_size=1 // 0: return // and at the class level a Declare attribute: // org.aspectj.weaver.Declare: length = 0x51 // 05 00 00 00 03 01 00 05 40 41 6E 6E 6F 01 00 17 // 61 6A 63 24 64 65 63 6C 61 72 65 5F 61 74 5F 6D // 65 74 68 6F 64 5F 31 01 01 00 00 00 00 05 05 00 // 08 73 61 79 48 65 6C 6C 6F 00 01 04 00 00 00 00 // 07 00 00 00 27 00 00 00 34 00 00 00 16 00 00 00 // 3C AnnotationAJ constructedAnnotation = buildDeclareAnnotation_actualAnnotation(cg, da); if (constructedAnnotation==null) { return; // error occurred (and was reported), do not continue } String nameComponent = da.declareAnnotationKind.name().toLowerCase(); String declareName = new StringBuilder("ajc$declare_at_").append(nameComponent).append("_").append(decCounter).toString(); LazyMethodGen declareMethod = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, declareName, NO_ARGS, EMPTY_STRINGS, cg); InstructionList declareMethodBody = declareMethod.getBody(); declareMethodBody.append(InstructionFactory.RETURN); declareMethod.addAnnotation(constructedAnnotation); DeclareAnnotation deca = null; ITokenSource tokenSource = BasicTokenSource.makeTokenSource(da.pattern,null); PatternParser pp = new PatternParser(tokenSource); if (da.declareAnnotationKind==DeclareAnnotationKind.Method || da.declareAnnotationKind==DeclareAnnotationKind.Field) { ISignaturePattern isp = (da.declareAnnotationKind==DeclareAnnotationKind.Method?pp.parseCompoundMethodOrConstructorSignaturePattern(true):pp.parseCompoundFieldSignaturePattern()); deca = new DeclareAnnotation(da.declareAnnotationKind==DeclareAnnotationKind.Method?DeclareAnnotation.AT_METHOD:DeclareAnnotation.AT_FIELD, isp); } else if (da.declareAnnotationKind==DeclareAnnotationKind.Type) { TypePattern tp = pp.parseTypePattern(); deca = new DeclareAnnotation(DeclareAnnotation.AT_TYPE,tp); } deca.setAnnotationMethod(declareName); deca.setAnnotationString(da.annotation); AjAttribute attribute = new AjAttribute.DeclareAttribute(deca); cg.addAttribute(attribute); cg.addMethodGen(declareMethod); } /** * Construct the annotation that the declare wants to add to the target. */ private AnnotationAJ buildDeclareAnnotation_actualAnnotation(LazyClassGen cg, Definition.DeclareAnnotation da) { AnnotationGen anno = buildAnnotationFromString(cg.getConstantPool(),cg.getWorld(),da.annotation); if (anno==null) { return null; } else { AnnotationAJ bcelAnnotation = new BcelAnnotation(anno, world); return bcelAnnotation; } } // TODO support array values // TODO support annotation values /** * Build an AnnotationGen object based on a string, for example "@Foo(35,message='string')". Notice single quotes are fine for * strings as they don't need special handling in the XML. */ private AnnotationGen buildAnnotationFromString(ConstantPool cp, World w, String annotationString) { int paren = annotationString.indexOf('('); if (paren==-1) { // the easy case, no values AnnotationGen aaj = buildBaseAnnotationType(cp,world,annotationString); return aaj; } else { // Discover the name and name/value pairs String name = annotationString.substring(0,paren); // break the rest into pieces based on the commas List values = new ArrayList<>(); int pos = paren+1; int depth = 0; int len = annotationString.length(); int start = pos; while (pos elems = new ArrayList<>(); return new AnnotationGen(new ObjectType(annoname), elems, true, cp); } /** * Construct the annotation that indicates this is a declare */ /** * The PointcutAndAdvice object encapsulates an advice kind, a pointcut and names a Java method in a particular class. Generate * an annotation style advice that has that pointcut whose implementation delegates to the Java method. */ private void generateAdviceMethod(PointcutAndAdvice paa, int adviceCounter, LazyClassGen cg) { // Check: Verify the class to be called does exist: ResolvedType delegateClass = world.resolve(UnresolvedType.forName(paa.adviceClass)); if (delegateClass.isMissing()) { reportError("Class to invoke cannot be found: '" + paa.adviceClass + "'"); return; } // Generate a name for this advice, includes advice kind plus a counter String adviceName = new StringBuilder("generated$").append(paa.adviceKind.toString().toLowerCase()).append("$advice$") .append(adviceCounter).toString(); // Build the annotation that encapsulates the pointcut AnnotationAJ aaj = buildAdviceAnnotation(cg, paa); // Chop up the supplied advice method string into its pieces. // Example: foo(JoinPoint jp, java.lang.String string) // JoinPoint and friends are recognized (so dont need fq package) String method = paa.adviceMethod; int paren = method.indexOf("("); String methodName = method.substring(0, paren); String signature = method.substring(paren); // Check: signature looks ok if (signature.charAt(0) != '(' || !signature.endsWith(")")) { reportError("Badly formatted parameter signature: '" + method + "'"); return; } // Extract parameter types and names List paramTypes = new ArrayList<>(); List paramNames = new ArrayList<>(); if (signature.charAt(1) != ')') { // there are parameters to convert into a signature StringBuilder convertedSignature = new StringBuilder("("); boolean paramsBroken = false; int pos = 1; while (pos < signature.length() && signature.charAt(pos) != ')' && !paramsBroken) { int nextChunkEndPos = signature.indexOf(',', pos); if (nextChunkEndPos == -1) { nextChunkEndPos = signature.indexOf(')', pos); } // chunk will be a type plus a space plus a name String nextChunk = signature.substring(pos, nextChunkEndPos).trim(); int space = nextChunk.indexOf(" "); ResolvedType resolvedParamType = null; if (space == -1) { // There is no parameter name, hopefully not needed! if (nextChunk.equals("JoinPoint")) { nextChunk = "org.aspectj.lang.JoinPoint"; } else if (nextChunk.equals("JoinPoint.StaticPart")) { nextChunk = "org.aspectj.lang.JoinPoint$StaticPart"; } else if (nextChunk.equals("ProceedingJoinPoint")) { nextChunk = "org.aspectj.lang.ProceedingJoinPoint"; } UnresolvedType unresolvedParamType = UnresolvedType.forName(nextChunk); resolvedParamType = world.resolve(unresolvedParamType); } else { String typename = nextChunk.substring(0, space); if (typename.equals("JoinPoint")) { typename = "org.aspectj.lang.JoinPoint"; } else if (typename.equals("JoinPoint.StaticPart")) { typename = "org.aspectj.lang.JoinPoint$StaticPart"; } else if (typename.equals("ProceedingJoinPoint")) { typename = "org.aspectj.lang.ProceedingJoinPoint"; } UnresolvedType unresolvedParamType = UnresolvedType.forName(typename); resolvedParamType = world.resolve(unresolvedParamType); String paramname = nextChunk.substring(space).trim(); paramNames.add(paramname); } if (resolvedParamType.isMissing()) { reportError("Cannot find type specified as parameter: '" + nextChunk + "' from signature '" + signature + "'"); paramsBroken = true; } paramTypes.add(Type.getType(resolvedParamType.getSignature())); convertedSignature.append(resolvedParamType.getSignature()); pos = nextChunkEndPos + 1; } convertedSignature.append(")"); signature = convertedSignature.toString(); if (paramsBroken) { return; } } Type returnType = Type.VOID; // If around advice we must find the actual delegate method and use its return type if (paa.adviceKind == AdviceKind.Around) { ResolvedMember[] methods = delegateClass.getDeclaredMethods(); ResolvedMember found = null; for (ResolvedMember candidate : methods) { if (candidate.getName().equals(methodName)) { UnresolvedType[] cparms = candidate.getParameterTypes(); if (cparms.length == paramTypes.size()) { boolean paramsMatch = true; for (int i = 0; i < cparms.length; i++) { if (!cparms[i].getSignature().equals(paramTypes.get(i).getSignature())) { paramsMatch = false; break; } } if (paramsMatch) { found = candidate; break; } } } } if (found != null) { returnType = Type.getType(found.getReturnType().getSignature()); } else { reportError("Unable to find method to invoke. In class: " + delegateClass.getName() + " cant find " + paa.adviceMethod); return; } } // Time to construct the method itself: LazyMethodGen advice = new LazyMethodGen(Modifier.PUBLIC, returnType, adviceName, paramTypes.toArray(NO_ARGS), EMPTY_STRINGS, cg); InstructionList adviceBody = advice.getBody(); // Generate code to load the parameters int pos = 1; // first slot after 'this' for (Type paramType : paramTypes) { adviceBody.append(InstructionFactory.createLoad(paramType, pos)); pos += paramType.getSize(); } // Generate the delegate call adviceBody.append(cg.getFactory().createInvoke(paa.adviceClass, methodName, signature + returnType.getSignature(), Constants.INVOKESTATIC)); // Generate the right return if (returnType == Type.VOID) { adviceBody.append(InstructionConstants.RETURN); } else { if (returnType.getSignature().length() < 2) { String sig = returnType.getSignature(); if (sig.equals("F")) { adviceBody.append(InstructionConstants.FRETURN); } else if (sig.equals("D")) { adviceBody.append(InstructionConstants.DRETURN); } else if (sig.equals("J")) { adviceBody.append(InstructionConstants.LRETURN); } else { adviceBody.append(InstructionConstants.IRETURN); } } else { adviceBody.append(InstructionConstants.ARETURN); } } // Add the annotation advice.addAnnotation(aaj); InstructionHandle start = adviceBody.getStart(); // Setup the local variable targeters so that the binding will work String sig = concreteAspect.name.replace('.', '/'); start.addTargeter(new LocalVariableTag("L" + sig + ";", "this", 0, start.getPosition())); if (paramNames.size() > 0) { for (int i = 0; i < paramNames.size(); i++) { start.addTargeter(new LocalVariableTag(paramTypes.get(i).getSignature(), paramNames.get(i), i + 1, start .getPosition())); } } // Record the new method in the class cg.addMethodGen(advice); } /** * For the given PointcutAndAdvice build the correct advice annotation. */ private AnnotationAJ buildAdviceAnnotation(LazyClassGen cg, PointcutAndAdvice paa) { SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), paa.pointcut); List elems = new ArrayList<>(); elems.add(new NameValuePair("value", svg, cg.getConstantPool())); AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/" + paa.adviceKind.toString()), elems, true, cg.getConstantPool()); AnnotationAJ aaj = new BcelAnnotation(mag, world); return aaj; } /** * Error reporting * * @param message */ private void reportError(String message) { world.getMessageHandler().handleMessage(new Message(message, IMessage.ERROR, null, null)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy