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

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

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 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]; private final static Type[] EMPTY_TYPES = new Type[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() { StringBuffer sb = new StringBuffer(" 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, "", EMPTY_TYPES, EMPTY_STRINGS, cg); InstructionList cbody = init.getBody(); cbody.append(InstructionConstants.ALOAD_0); cbody.append(cg.getFactory().createInvoke(parentName, "", Type.VOID, EMPTY_TYPES, 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, EMPTY_TYPES, 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, Type.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(new Type[0]), 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