@@ -78,6 +78,8 @@ public interface Member extends Comparable<Member> { | |||
*/ | |||
public boolean canBeParameterized(); | |||
public AnnotationAJ[][] getParameterAnnotations(); | |||
public AnnotationAJ[] getAnnotations(); | |||
public Collection<ResolvedType> getDeclaringTypes(World world); |
@@ -506,6 +506,11 @@ public class MemberImpl implements Member { | |||
} | |||
return resolved.getParameterNames(); | |||
} | |||
public AnnotationAJ[][] getParameterAnnotations() { | |||
throw new UnsupportedOperationException("You should resolve this member '" + this | |||
+ "' and call getParameterAnnotations() on the result..."); | |||
} | |||
/** | |||
* All the signatures that a join point with this member as its signature has. |
@@ -236,6 +236,8 @@ public abstract class Shadow { | |||
public abstract Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType); | |||
public abstract Var getArgParamAnnotationVar(int i, UnresolvedType forAnnotationType); | |||
public abstract Member getEnclosingCodeSignature(); | |||
/** |
@@ -14,10 +14,12 @@ import java.util.List; | |||
import java.util.Map; | |||
import org.aspectj.util.FuzzyBoolean; | |||
import org.aspectj.weaver.AnnotationAJ; | |||
import org.aspectj.weaver.CompressingDataOutputStream; | |||
import org.aspectj.weaver.ISourceContext; | |||
import org.aspectj.weaver.IntMap; | |||
import org.aspectj.weaver.ResolvedType; | |||
import org.aspectj.weaver.Shadow; | |||
import org.aspectj.weaver.UnresolvedType; | |||
import org.aspectj.weaver.VersionedDataInputStream; | |||
import org.aspectj.weaver.World; | |||
@@ -72,10 +74,11 @@ public class AnnotationPatternList extends PatternNode { | |||
typePatterns[i].resolve(inWorld); | |||
} | |||
} | |||
public FuzzyBoolean matches(ResolvedType[] someArgs) { | |||
public FuzzyBoolean matches(ResolvedType[] parameterTypes, Shadow shadow) { | |||
ResolvedType[][] paramAnnotationTypes = null; | |||
// do some quick length tests first | |||
int numArgsMatchedByEllipsis = (someArgs.length + ellipsisCount) - typePatterns.length; | |||
int numArgsMatchedByEllipsis = (parameterTypes.length + ellipsisCount) - typePatterns.length; | |||
if (numArgsMatchedByEllipsis < 0) { | |||
return FuzzyBoolean.NO; | |||
} | |||
@@ -91,14 +94,26 @@ public class AnnotationPatternList extends PatternNode { | |||
argsIndex += numArgsMatchedByEllipsis; | |||
} else if (typePatterns[i] == AnnotationTypePattern.ANY) { | |||
argsIndex++; | |||
} else if (typePatterns[i].isForParameterAnnotationMatch()) { | |||
if (paramAnnotationTypes == null) { | |||
paramAnnotationTypes = shadow.getResolvedSignature().getParameterAnnotationTypes(); | |||
} | |||
ExactAnnotationTypePattern ap = (ExactAnnotationTypePattern) typePatterns[i]; | |||
FuzzyBoolean matches = ap.matches(null, paramAnnotationTypes.length==0?ResolvedType.NONE:paramAnnotationTypes[argsIndex]); | |||
if (matches == FuzzyBoolean.NO) { | |||
return FuzzyBoolean.NO; | |||
} else { | |||
argsIndex++; | |||
ret = ret.and(matches); | |||
} | |||
} else { | |||
// match the argument type at argsIndex with the ExactAnnotationTypePattern | |||
// we know it is exact because nothing else is allowed in args | |||
if (someArgs[argsIndex].isPrimitiveType()) { | |||
if (parameterTypes[argsIndex].isPrimitiveType()) { | |||
return FuzzyBoolean.NO; // can never match | |||
} | |||
ExactAnnotationTypePattern ap = (ExactAnnotationTypePattern) typePatterns[i]; | |||
FuzzyBoolean matches = ap.matchesRuntimeType(someArgs[argsIndex]); | |||
FuzzyBoolean matches = ap.matchesRuntimeType(parameterTypes[argsIndex]); | |||
if (matches == FuzzyBoolean.NO) { | |||
return FuzzyBoolean.MAYBE; // could still match at runtime | |||
} else { | |||
@@ -110,6 +125,47 @@ public class AnnotationPatternList extends PatternNode { | |||
return ret; | |||
} | |||
public FuzzyBoolean matches(ResolvedType[] someArgs) { | |||
// do some quick length tests first | |||
int numArgsMatchedByEllipsis = (someArgs.length + ellipsisCount) - typePatterns.length; | |||
if (numArgsMatchedByEllipsis < 0) { | |||
return FuzzyBoolean.NO; | |||
} | |||
if ((numArgsMatchedByEllipsis > 0) && (ellipsisCount == 0)) { | |||
return FuzzyBoolean.NO; | |||
} | |||
// now work through the args and the patterns, skipping at ellipsis | |||
FuzzyBoolean ret = FuzzyBoolean.YES; | |||
int argsIndex = 0; | |||
for (int i = 0; i < typePatterns.length; i++) { | |||
if (typePatterns[i] == AnnotationTypePattern.ELLIPSIS) { | |||
// match ellipsisMatchCount args | |||
argsIndex += numArgsMatchedByEllipsis; | |||
} else if (typePatterns[i] == AnnotationTypePattern.ANY) { | |||
argsIndex++; | |||
} else { | |||
// match the argument type at argsIndex with the ExactAnnotationTypePattern | |||
// we know it is exact because nothing else is allowed in args | |||
if (someArgs[argsIndex].isPrimitiveType()) { | |||
return FuzzyBoolean.NO; // can never match | |||
} | |||
ExactAnnotationTypePattern ap = (ExactAnnotationTypePattern) typePatterns[i]; | |||
// if (ap.isForParameterAnnotationMatch()) { | |||
// ap.matches(null,) | |||
// } else { | |||
FuzzyBoolean matches = ap.matchesRuntimeType(someArgs[argsIndex]); | |||
if (matches == FuzzyBoolean.NO) { | |||
return FuzzyBoolean.MAYBE; // could still match at runtime | |||
} else { | |||
argsIndex++; | |||
ret = ret.and(matches); | |||
} | |||
// } | |||
} | |||
} | |||
return ret; | |||
} | |||
public int size() { | |||
return typePatterns.length; | |||
} |
@@ -76,15 +76,11 @@ public class ArgsAnnotationPointcut extends NameBindingPointcut { | |||
return FuzzyBoolean.MAYBE; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) | |||
*/ | |||
protected FuzzyBoolean matchInternal(Shadow shadow) { | |||
arguments.resolve(shadow.getIWorld()); | |||
FuzzyBoolean ret = arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes())); | |||
return ret; | |||
FuzzyBoolean ret1 = arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes()),shadow); | |||
// FuzzyBoolean ret2 = arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes())); | |||
return ret1; | |||
} | |||
/* | |||
@@ -166,12 +162,22 @@ public class ArgsAnnotationPointcut extends NameBindingPointcut { | |||
ResolvedType rAnnType = ap.getAnnotationType().resolve(shadow.getIWorld()); | |||
if (ap instanceof BindingAnnotationTypePattern) { | |||
BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) ap; | |||
Var annvar = shadow.getArgAnnotationVar(argsIndex, rAnnType); | |||
state.set(btp.getFormalIndex(), annvar); | |||
Var v = btp.isForParameterAnnotationMatch() ? | |||
shadow.getArgParamAnnotationVar(argsIndex, rAnnType) : | |||
shadow.getArgAnnotationVar(argsIndex, rAnnType); | |||
state.set(btp.getFormalIndex(), v); | |||
} | |||
if (!ap.matches(rArgType).alwaysTrue()) { | |||
// we need a test... | |||
ret = Test.makeAnd(ret, Test.makeHasAnnotation(shadow.getArgVar(argsIndex), rAnnType)); | |||
if (ap.isForParameterAnnotationMatch()) { | |||
if (!ap.matches(null,new ResolvedType[] {rAnnType}).alwaysTrue()) { | |||
if (true) throw new IllegalStateException(); | |||
// need something intelligent here... | |||
ret = Test.makeAnd(ret, Test.makeHasAnnotation(shadow.getArgVar(argsIndex), rAnnType)); | |||
} | |||
} else { | |||
if (!ap.matches(rAnnType).alwaysTrue()) { | |||
// we need a test... | |||
ret = Test.makeAnd(ret, Test.makeHasAnnotation(shadow.getArgVar(argsIndex), rAnnType)); | |||
} | |||
} | |||
argsIndex++; | |||
} |
@@ -29,9 +29,6 @@ public class BindingAnnotationTypePattern extends ExactAnnotationTypePattern imp | |||
protected int formalIndex; | |||
/** | |||
* @param annotationType | |||
*/ | |||
public BindingAnnotationTypePattern(UnresolvedType annotationType, int index) { | |||
super(annotationType, null); | |||
this.formalIndex = index; | |||
@@ -107,12 +104,17 @@ public class BindingAnnotationTypePattern extends ExactAnnotationTypePattern imp | |||
} | |||
public AnnotationTypePattern remapAdviceFormals(IntMap bindings) { | |||
AnnotationTypePattern atp; | |||
if (!bindings.hasKey(formalIndex)) { | |||
return new ExactAnnotationTypePattern(annotationType, null); | |||
atp = new ExactAnnotationTypePattern(annotationType, null); | |||
} else { | |||
int newFormalIndex = bindings.get(formalIndex); | |||
return new BindingAnnotationTypePattern(annotationType, newFormalIndex); | |||
atp = new BindingAnnotationTypePattern(annotationType, newFormalIndex); | |||
if (this.isForParameterAnnotationMatch()) { | |||
atp.setForParameterAnnotationMatch(); | |||
} | |||
} | |||
return atp; | |||
} | |||
private static final byte VERSION = 1; // rev if serialised form changed | |||
@@ -124,6 +126,7 @@ public class BindingAnnotationTypePattern extends ExactAnnotationTypePattern imp | |||
annotationType.write(s); | |||
s.writeShort((short) formalIndex); | |||
writeLocation(s); | |||
s.writeBoolean(isForParameterAnnotationMatch()); | |||
} | |||
public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { | |||
@@ -133,6 +136,8 @@ public class BindingAnnotationTypePattern extends ExactAnnotationTypePattern imp | |||
} | |||
AnnotationTypePattern ret = new BindingAnnotationTypePattern(UnresolvedType.read(s), s.readShort()); | |||
ret.readLocation(context, s); | |||
if (s.readBoolean()) | |||
ret.setForParameterAnnotationMatch(); | |||
return ret; | |||
} | |||
} |
@@ -43,6 +43,7 @@ public class ExactAnnotationTypePattern extends AnnotationTypePattern { | |||
protected boolean resolved = false; | |||
protected boolean bindingPattern = false; | |||
private Map<String, String> annotationValues; | |||
private int annotationNumber; | |||
// OPTIMIZE is annotationtype really unresolved???? surely it is resolved by | |||
// now... | |||
@@ -232,6 +233,7 @@ public class ExactAnnotationTypePattern extends AnnotationTypePattern { | |||
.error("Compiler limitation: annotation value matching for parameter annotations not yet supported")); | |||
return FuzzyBoolean.NO; | |||
} | |||
this.annotationNumber= i; | |||
return FuzzyBoolean.YES; | |||
} | |||
} |
@@ -1174,9 +1174,15 @@ public class PatternParser { | |||
p.setLocation(sourceContext, startPos, endPos); | |||
// For optimized syntax that allows binding directly to annotation values (pr234943) | |||
if (maybeEat("(")) { | |||
String formalName = parseIdentifier(); | |||
p = new ExactAnnotationFieldTypePattern(p, formalName); | |||
eat(")"); | |||
if (maybeEat("*")) { | |||
// Attempt to bind parameter annotation: @args(@SomeParamAnnotation (*),..) | |||
p.setForParameterAnnotationMatch(); | |||
eat(")"); | |||
} else { | |||
String formalName = parseIdentifier(); | |||
p = new ExactAnnotationFieldTypePattern(p, formalName); | |||
eat(")"); | |||
} | |||
} | |||
return p; | |||
} |
@@ -1,5 +1,5 @@ | |||
/* ******************************************************************* | |||
* Copyright (c) 2005 Contributors. | |||
* Copyright (c) 2005-2016 Contributors. | |||
* All rights reserved. | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Eclipse Public License v1.0 | |||
@@ -20,7 +20,10 @@ import org.aspectj.weaver.UnresolvedType; | |||
import org.aspectj.weaver.World; | |||
/** | |||
* @author colyer Used in 1.4 code to access annotations safely | |||
* Used in 1.4 code to access annotations safely. | |||
* | |||
* @author Adrian Colyer | |||
* @author Andy Clement | |||
*/ | |||
public interface AnnotationFinder { | |||
@@ -32,14 +35,15 @@ public interface AnnotationFinder { | |||
Object getAnnotationFromMember(ResolvedType annotationType, Member aMember); | |||
public AnnotationAJ getAnnotationOfType(UnresolvedType ofType, | |||
Member onMember); | |||
AnnotationAJ getAnnotationOfType(UnresolvedType ofType, Member onMember); | |||
public String getAnnotationDefaultValue(Member onMember); | |||
String getAnnotationDefaultValue(Member onMember); | |||
Object getAnnotationFromClass(ResolvedType annotationType, Class aClass); | |||
Object getAnnotationFromClass(ResolvedType annotationType, Class<?> aClass); | |||
Set/* ResolvedType */getAnnotations(Member onMember); | |||
Set<ResolvedType> getAnnotations(Member onMember); | |||
ResolvedType[][] getParameterAnnotationTypes(Member onMember); | |||
Object getParamAnnotation(Member subject, int argsIndex, int paramAnnoIndex); | |||
} |
@@ -28,8 +28,8 @@ import org.aspectj.weaver.ast.Var; | |||
import org.aspectj.weaver.tools.MatchingContext; | |||
/** | |||
* @author colyer | |||
* | |||
* @author Adrian Colyer | |||
* @author Andy Clement | |||
*/ | |||
public class ReflectionShadow extends Shadow { | |||
@@ -42,10 +42,11 @@ public class ReflectionShadow extends Shadow { | |||
private Var[] argsVars = null; | |||
private Var atThisVar = null; | |||
private Var atTargetVar = null; | |||
private Map atArgsVars = new HashMap(); | |||
private Map withinAnnotationVar = new HashMap(); | |||
private Map withinCodeAnnotationVar = new HashMap(); | |||
private Map annotationVar = new HashMap(); | |||
private Map<ResolvedType,Var[]> atArgsVars = new HashMap<>(); | |||
private Map<ResolvedType,Var[]> atArgParamVars = new HashMap<>(); | |||
private Map<ResolvedType,Var> withinAnnotationVar = new HashMap<>(); | |||
private Map<ResolvedType,Var> withinCodeAnnotationVar = new HashMap<>(); | |||
private Map<ResolvedType,Var> annotationVar = new HashMap<>(); | |||
private AnnotationFinder annotationFinder; | |||
public static Shadow makeExecutionShadow(World inWorld, java.lang.reflect.Member forMethod, MatchingContext withContext) { | |||
@@ -311,11 +312,6 @@ public class ReflectionShadow extends Shadow { | |||
return atTargetVar; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see org.aspectj.weaver.Shadow#getArgAnnotationVar(int, org.aspectj.weaver.UnresolvedType) | |||
*/ | |||
public Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType) { | |||
ResolvedType annType = forAnnotationType.resolve(world); | |||
if (atArgsVars.get(annType) == null) { | |||
@@ -330,6 +326,27 @@ public class ReflectionShadow extends Shadow { | |||
} | |||
return vars[i]; | |||
} | |||
public Var getArgParamAnnotationVar(int i, UnresolvedType forAnnotationType) { | |||
ResolvedType resolvedAnnotationType = forAnnotationType.resolve(world); | |||
if (atArgParamVars.get(resolvedAnnotationType) == null) { | |||
Var[] vars = new Var[getArgCount()]; | |||
atArgParamVars.put(resolvedAnnotationType, vars); | |||
} | |||
Var[] vars = (Var[]) atArgParamVars.get(resolvedAnnotationType); | |||
if (i > (vars.length - 1)) | |||
return null; | |||
if (vars[i] == null) { | |||
ResolvedMember rm = getSignature().resolve(world); | |||
ResolvedType[] paramAnnos = rm.getParameterAnnotationTypes()[i]; | |||
for (int j=0;j<paramAnnos.length;j++) { | |||
if (paramAnnos[j].equals(resolvedAnnotationType)) { | |||
vars[i] = ReflectionVar.createArgsParamAnnotationVar(resolvedAnnotationType, i, j, this.annotationFinder); | |||
} | |||
} | |||
} | |||
return vars[i]; | |||
} | |||
/* | |||
* (non-Javadoc) |
@@ -30,6 +30,7 @@ public final class ReflectionVar extends Var { | |||
static final int AT_WITHIN_VAR = 6; | |||
static final int AT_WITHINCODE_VAR = 7; | |||
static final int AT_ANNOTATION_VAR = 8; | |||
static final int AT_ARGS_PARAM_ANNO_VAR = 9; | |||
private AnnotationFinder annotationFinder = null; | |||
@@ -48,6 +49,7 @@ public final class ReflectionVar extends Var { | |||
// } | |||
private int argsIndex = 0; | |||
private int paramAnnoIndex; | |||
private int varType; | |||
public static ReflectionVar createThisVar(ResolvedType type,AnnotationFinder finder) { | |||
@@ -87,6 +89,14 @@ public final class ReflectionVar extends Var { | |||
ret.argsIndex = index; | |||
return ret; | |||
} | |||
public static ReflectionVar createArgsParamAnnotationVar(ResolvedType type, int index, int paramAnnoIndex, AnnotationFinder finder) { | |||
ReflectionVar ret = new ReflectionVar(type,finder); | |||
ret.varType = AT_ARGS_PARAM_ANNO_VAR; | |||
ret.argsIndex = index; | |||
ret.paramAnnoIndex = paramAnnoIndex; | |||
return ret; | |||
} | |||
public static ReflectionVar createWithinAnnotationVar(ResolvedType annType, AnnotationFinder finder) { | |||
ReflectionVar ret = new ReflectionVar(annType,finder); | |||
@@ -106,7 +116,7 @@ public final class ReflectionVar extends Var { | |||
return ret; | |||
} | |||
private ReflectionVar(ResolvedType type,AnnotationFinder finder) { | |||
private ReflectionVar(ResolvedType type, AnnotationFinder finder) { | |||
super(type); | |||
this.annotationFinder = finder; | |||
} | |||
@@ -115,9 +125,11 @@ public final class ReflectionVar extends Var { | |||
public Object getBindingAtJoinPoint(Object thisObject, Object targetObject, Object[] args) { | |||
return getBindingAtJoinPoint(thisObject,targetObject,args,null,null,null); | |||
} | |||
/** | |||
* At a join point with the given this, target, and args, return the object to which this | |||
* var is bound. | |||
* | |||
* @param thisObject | |||
* @param targetObject | |||
* @param args | |||
@@ -149,6 +161,11 @@ public final class ReflectionVar extends Var { | |||
if (annotationFinder != null) { | |||
return annotationFinder.getAnnotation(getType(), args[argsIndex]); | |||
} else return null; | |||
case AT_ARGS_PARAM_ANNO_VAR: | |||
if (this.argsIndex > (args.length - 1)) return null; | |||
if (annotationFinder != null) { | |||
return annotationFinder.getParamAnnotation(subject, argsIndex, paramAnnoIndex); | |||
} else return null; | |||
case AT_WITHIN_VAR: | |||
if (annotationFinder != null) { | |||
return annotationFinder.getAnnotationFromClass(getType(), withinType); |
@@ -44,10 +44,10 @@ public class StandardShadow extends Shadow { | |||
private Var[] argsVars = null; | |||
private Var atThisVar = null; | |||
private Var atTargetVar = null; | |||
private Map atArgsVars = new HashMap(); | |||
private Map withinAnnotationVar = new HashMap(); | |||
private Map withinCodeAnnotationVar = new HashMap(); | |||
private Map annotationVar = new HashMap(); | |||
private Map<ResolvedType,Var[]> atArgsVars = new HashMap<>(); | |||
private Map<ResolvedType,Var> withinAnnotationVar = new HashMap<>(); | |||
private Map<ResolvedType,Var> withinCodeAnnotationVar = new HashMap<>(); | |||
private Map<ResolvedType,Var> annotationVar = new HashMap<>(); | |||
private AnnotationFinder annotationFinder; | |||
public static Shadow makeExecutionShadow(World inWorld, java.lang.reflect.Member forMethod, MatchingContext withContext) { |
@@ -0,0 +1,6 @@ | |||
import java.lang.annotation.*; | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface ColouredAnnotation { | |||
RGB value(); | |||
} |
@@ -0,0 +1,5 @@ | |||
import java.lang.annotation.*; | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface Filler { | |||
} |
@@ -0,0 +1,3 @@ | |||
public enum RGB { | |||
RED, GREEN, BLUE; | |||
} |
@@ -0,0 +1,13 @@ | |||
public class Test1 { | |||
public static void main(String[] argv) { | |||
coloured("abc"); | |||
} | |||
public static void coloured(@ColouredAnnotation(RGB.RED) String param1) {} | |||
} | |||
aspect X { | |||
// execution(@ColouredAnnotation * colouredMethod(..)) && @annotation(ColouredAnnotation(colour)); | |||
before(ColouredAnnotation ca): execution(* *(@ColouredAnnotation (*))) && @args(ca (*)) { | |||
System.out.println("Annotation from parameter on method "+thisJoinPoint+" is "+ca); | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
public class Test2 { | |||
public static void main(String[] argv) { | |||
coloured("abc"); | |||
} | |||
public static void coloured(@ColouredAnnotation(RGB.GREEN) String param1) {} | |||
} | |||
aspect X { | |||
// execution(@ColouredAnnotation * colouredMethod(..)) && @annotation(ColouredAnnotation(colour)); | |||
before(ColouredAnnotation ca): execution(* *(..)) && @args(ca (*)) { | |||
System.out.println("Annotation from parameter on method "+thisJoinPoint+" is "+ca); | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
public class Test3 { | |||
public static void main(String[] argv) { | |||
coloured(1,"abc"); | |||
} | |||
public static void coloured(int param1, @ColouredAnnotation(RGB.RED) String param2) {} | |||
} | |||
aspect X { | |||
// execution(@ColouredAnnotation * colouredMethod(..)) && @annotation(ColouredAnnotation(colour)); | |||
before(ColouredAnnotation ca): execution(* *(..)) && @args(*, ca (*)) { | |||
System.out.println("Annotation from parameter on method "+thisJoinPoint+" is "+ca); | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
public class Test4 { | |||
public static void main(String[] argv) { | |||
coloured(1,"abc"); | |||
} | |||
public static void coloured(int param1, @Filler @ColouredAnnotation(RGB.GREEN) String param2) {} | |||
} | |||
aspect X { | |||
// execution(@ColouredAnnotation * colouredMethod(..)) && @annotation(ColouredAnnotation(colour)); | |||
before(ColouredAnnotation ca): execution(* *(..)) && @args(*, ca (*)) { | |||
System.out.println("Annotation from parameter on method "+thisJoinPoint+" is "+ca); | |||
} | |||
} |
@@ -24,6 +24,42 @@ import junit.framework.Test; | |||
*/ | |||
public class Ajc1810Tests extends org.aspectj.testing.XMLBasedAjcTestCase { | |||
// Basic with execution(* *(@Foo (*))) && @args(ca (*)) | |||
public void testParamAnnoBinding_259416_1() { | |||
runTest("param anno binding"); | |||
} | |||
// Basic with execution(* *(..)) && args(ca (*)) | |||
public void testParamAnnoBinding_259416_2() { | |||
runTest("param anno binding 2"); | |||
} | |||
// Basic with execution(* *(..)) && args(*,ca (*)) | |||
public void testParamAnnoBinding_259416_3() { | |||
runTest("param anno binding 3"); | |||
} | |||
// Basic with execution(* *(..)) && args(*,ca (*)) where target has two parameter annotations and we match the 2nd one | |||
public void testParamAnnoBinding_259416_4() { | |||
runTest("param anno binding 4"); | |||
} | |||
// execution(* *(..)) && args(f *,ca (*)) | |||
// args(ca1 (*),ca2 (*)) | |||
// args(ca(*)) | |||
// args(ca1 (f)) | |||
// args(ca1 ba1 (*)) | |||
// args(ca1 ba1 (f1 f2)) | |||
// args(ca1 (*), .., ca2 (*)) | |||
public void testBinding_500035() { | |||
runTest("ataspectj binding"); | |||
} |
@@ -2,6 +2,51 @@ | |||
<suite> | |||
<ajc-test dir="bugs1810/259416" title="param anno binding"> | |||
<compile options="-1.8 -showWeaveInfo" files="RGB.java ColouredAnnotation.java Test1.java"> | |||
<message kind="weave" text="Join point 'method-execution(void Test1.coloured(java.lang.String))' in Type 'Test1' (Test1.java:5) advised by before advice from 'X' (Test1.java:10)"/> | |||
</compile> | |||
<run class="Test1"> | |||
<stdout> | |||
<line text="Annotation from parameter on method execution(void Test1.coloured(String)) is @ColouredAnnotation(value=RED)"/> | |||
</stdout> | |||
</run> | |||
</ajc-test> | |||
<ajc-test dir="bugs1810/259416" title="param anno binding 2"> | |||
<compile options="-1.8 -showWeaveInfo" files="RGB.java ColouredAnnotation.java Test2.java"> | |||
<message kind="weave" text="Join point 'method-execution(void Test2.coloured(java.lang.String))' in Type 'Test2' (Test2.java:5) advised by before advice from 'X' (Test2.java:10)"/> | |||
</compile> | |||
<run class="Test2"> | |||
<stdout> | |||
<line text="Annotation from parameter on method execution(void Test2.coloured(String)) is @ColouredAnnotation(value=GREEN)"/> | |||
</stdout> | |||
</run> | |||
</ajc-test> | |||
<ajc-test dir="bugs1810/259416" title="param anno binding 3"> | |||
<compile options="-1.8 -showWeaveInfo" files="RGB.java ColouredAnnotation.java Test3.java"> | |||
<message kind="weave" text="Join point 'method-execution(void Test3.coloured(int, java.lang.String))' in Type 'Test3' (Test3.java:5) advised by before advice from 'X' (Test3.java:10)"/> | |||
</compile> | |||
<run class="Test3"> | |||
<stdout> | |||
<line text="Annotation from parameter on method execution(void Test3.coloured(int, String)) is @ColouredAnnotation(value=RED)"/> | |||
</stdout> | |||
</run> | |||
</ajc-test> | |||
<ajc-test dir="bugs1810/259416" title="param anno binding 4"> | |||
<compile options="-1.8 -showWeaveInfo" files="RGB.java ColouredAnnotation.java Test4.java Filler.java"> | |||
<message kind="weave" text="Join point 'method-execution(void Test4.coloured(int, java.lang.String))' in Type 'Test4' (Test4.java:5) advised by before advice from 'X' (Test4.java:10)"/> | |||
</compile> | |||
<run class="Test4"> | |||
<stdout> | |||
<line text="Annotation from parameter on method execution(void Test4.coloured(int, String)) is @ColouredAnnotation(value=GREEN)"/> | |||
</stdout> | |||
</run> | |||
</ajc-test> | |||
<ajc-test dir="bugs1810/500035" title="ataspectj binding"> | |||
<compile options="-1.8" files="Code.java"/> | |||
<run class="Code"> |
@@ -875,6 +875,7 @@ public class BcelShadow extends Shadow { | |||
private BcelVar targetVar = null; | |||
private BcelVar[] argVars = null; | |||
private Map<ResolvedType, AnnotationAccessVar> kindedAnnotationVars = null; | |||
private Map<ResolvedType,AnnotationAccessVar>[] paramAnnoVars = null; | |||
private Map<ResolvedType, TypeAnnotationAccessVar> thisAnnotationVars = null; | |||
private Map<ResolvedType, TypeAnnotationAccessVar> targetAnnotationVars = null; | |||
// private Map/* <UnresolvedType,BcelVar> */[] argAnnotationVars = null; | |||
@@ -952,6 +953,12 @@ public class BcelShadow extends Shadow { | |||
return kindedAnnotationVars.get(forAnnotationType); | |||
} | |||
@Override | |||
public Var getArgParamAnnotationVar(int parameterIndex, UnresolvedType forAnnotationType) { | |||
initializeKindedAnnotationVars(); | |||
return paramAnnoVars[parameterIndex].get(forAnnotationType); | |||
} | |||
@Override | |||
public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) { | |||
initializeWithinAnnotationVars(); | |||
@@ -1538,6 +1545,41 @@ public class BcelShadow extends Shadow { | |||
} | |||
return foundMember.getAnnotationTypes(); | |||
} | |||
protected ResolvedType[][] getParameterAnnotations(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) { | |||
if (foundMember == null) { | |||
// check the ITD'd dooberries | |||
List<ConcreteTypeMunger> mungers = relevantType.resolve(world).getInterTypeMungers(); | |||
for (Iterator<ConcreteTypeMunger> iter = mungers.iterator(); iter.hasNext();) { | |||
Object munger = iter.next(); | |||
ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) munger; | |||
if (typeMunger.getMunger() instanceof NewMethodTypeMunger | |||
|| typeMunger.getMunger() instanceof NewConstructorTypeMunger) { | |||
ResolvedMember fakerm = typeMunger.getSignature(); | |||
// if (fakerm.hasAnnotations()) | |||
ResolvedMember ajcMethod = (getSignature().getKind() == ResolvedMember.CONSTRUCTOR ? AjcMemberMaker | |||
.postIntroducedConstructor(typeMunger.getAspectType(), fakerm.getDeclaringType(), | |||
fakerm.getParameterTypes()) : AjcMemberMaker.interMethodDispatcher(fakerm, | |||
typeMunger.getAspectType())); | |||
// AjcMemberMaker.interMethodBody(fakerm,typeMunger.getAspectType())); | |||
ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); | |||
if (fakerm.getName().equals(getSignature().getName()) | |||
&& fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) { | |||
relevantType = typeMunger.getAspectType(); | |||
foundMember = rmm; | |||
return foundMember.getParameterAnnotationTypes(); | |||
} | |||
} | |||
} | |||
// didn't find in ITDs, look in supers | |||
foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember); | |||
if (foundMember == null) { | |||
throw new IllegalStateException("Couldn't find member " + relevantMember + " for type " + relevantType); | |||
} | |||
} | |||
return foundMember.getParameterAnnotationTypes(); | |||
} | |||
/** | |||
* By determining what "kind" of shadow we are, we can find out the annotations on the appropriate element (method, field, | |||
@@ -1550,6 +1592,7 @@ public class BcelShadow extends Shadow { | |||
kindedAnnotationVars = new HashMap<ResolvedType, AnnotationAccessVar>(); | |||
ResolvedType[] annotations = null; | |||
ResolvedType[][] paramAnnotations = null; | |||
Member shadowSignature = getSignature(); | |||
Member annotationHolder = getSignature(); | |||
ResolvedType relevantType = shadowSignature.getDeclaringType().resolve(world); | |||
@@ -1564,6 +1607,7 @@ public class BcelShadow extends Shadow { | |||
} else if (getKind() == Shadow.MethodCall || getKind() == Shadow.ConstructorCall) { | |||
ResolvedMember foundMember = findMethod2(relevantType.resolve(world).getDeclaredMethods(), getSignature()); | |||
annotations = getAnnotations(foundMember, shadowSignature, relevantType); | |||
paramAnnotations = getParameterAnnotations(foundMember, shadowSignature, relevantType); | |||
annotationHolder = getRelevantMember(foundMember, shadowSignature, relevantType); | |||
relevantType = annotationHolder.getDeclaringType().resolve(world); | |||
} else if (getKind() == Shadow.FieldSet || getKind() == Shadow.FieldGet) { | |||
@@ -1589,17 +1633,15 @@ public class BcelShadow extends Shadow { | |||
} else if (getKind() == Shadow.MethodExecution || getKind() == Shadow.ConstructorExecution | |||
|| getKind() == Shadow.AdviceExecution) { | |||
ResolvedMember foundMember = findMethod2(relevantType.getDeclaredMethods(), getSignature()); | |||
annotations = getAnnotations(foundMember, shadowSignature, relevantType); | |||
paramAnnotations = getParameterAnnotations(foundMember, shadowSignature, relevantType); | |||
annotationHolder = getRelevantMember(foundMember, annotationHolder, relevantType); | |||
UnresolvedType ut = annotationHolder.getDeclaringType(); | |||
relevantType = ut.resolve(world); | |||
} else if (getKind() == Shadow.ExceptionHandler) { | |||
relevantType = getSignature().getParameterTypes()[0].resolve(world); | |||
annotations = relevantType.getAnnotationTypes(); | |||
} else if (getKind() == Shadow.PreInitialization || getKind() == Shadow.Initialization) { | |||
ResolvedMember found = findMethod2(relevantType.getDeclaredMethods(), getSignature()); | |||
annotations = found.getAnnotationTypes(); | |||
@@ -1615,6 +1657,22 @@ public class BcelShadow extends Shadow { | |||
annotationHolder, false); | |||
kindedAnnotationVars.put(annotationType, accessVar); | |||
} | |||
if (paramAnnotations != null) { | |||
int max = paramAnnotations.length; | |||
paramAnnoVars = new HashMap[max]; | |||
for (int p=0;p<max;p++) { | |||
ResolvedType[] annotationsOnParticularParam = paramAnnotations[p]; | |||
paramAnnoVars[p] = new HashMap<ResolvedType,AnnotationAccessVar>(); | |||
for (int i=0;i<annotationsOnParticularParam.length;i++) { | |||
// for (ResolvedType annotationOnParticularParam: annotationsOnParticularParam) { | |||
ResolvedType annotationOnParticularParam = annotationsOnParticularParam[i]; | |||
ParamAnnotationAccessVar paramAccessVar = new ParamAnnotationAccessVar(this, getKind(), annotationOnParticularParam, relevantType, | |||
annotationHolder, false, p, i); | |||
paramAnnoVars[p].put(annotationOnParticularParam, paramAccessVar); | |||
} | |||
} | |||
} | |||
} | |||
private ResolvedMember findMethod2(ResolvedMember members[], Member sig) { |
@@ -0,0 +1,206 @@ | |||
/* ******************************************************************* | |||
* Copyright (c) 2016 Contributors | |||
* All rights reserved. | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Eclipse Public License v1.0 | |||
* which accompanies this distribution and is available at | |||
* http://www.eclipse.org/legal/epl-v10.html | |||
* | |||
* Contributors: | |||
* Andy Clement initial implementation | |||
* ******************************************************************/ | |||
package org.aspectj.weaver.bcel; | |||
import org.aspectj.apache.bcel.Constants; | |||
import org.aspectj.apache.bcel.classfile.Field; | |||
import org.aspectj.apache.bcel.generic.Instruction; | |||
import org.aspectj.apache.bcel.generic.InstructionBranch; | |||
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.ObjectType; | |||
import org.aspectj.apache.bcel.generic.Type; | |||
import org.aspectj.weaver.Member; | |||
import org.aspectj.weaver.ResolvedType; | |||
import org.aspectj.weaver.Shadow; | |||
import org.aspectj.weaver.Shadow.Kind; | |||
import org.aspectj.weaver.UnresolvedType; | |||
import org.aspectj.weaver.ast.Var; | |||
/** | |||
* Represents access to an parameter annotation on a member element. | |||
*/ | |||
public class ParamAnnotationAccessVar extends AnnotationAccessVar { | |||
private BcelShadow shadow; | |||
private Kind kind; // What kind of shadow are we at? | |||
private UnresolvedType containingType; // The type upon which we want to ask for 'member' | |||
private Member member; // Holds the member that has the annotations (for method/field join points) | |||
private boolean isWithin; // implies @within() or @withincode(). If false, that implies @annotation() | |||
private int parameterNumber; | |||
private int annotationOnParameterNumber; | |||
public ParamAnnotationAccessVar(BcelShadow shadow, Kind kind, ResolvedType annotationType, | |||
UnresolvedType theTargetIsStoredHere, Member sig, boolean isWithin, int parameterNumber, int annotationOnParameterNumber) { | |||
super(shadow, kind, annotationType, theTargetIsStoredHere, sig, isWithin); | |||
this.shadow = shadow; | |||
this.kind = kind; | |||
this.containingType = theTargetIsStoredHere; | |||
this.member = sig; | |||
this.isWithin = isWithin; | |||
this.parameterNumber = parameterNumber; | |||
this.annotationOnParameterNumber = annotationOnParameterNumber; | |||
} | |||
@Override | |||
public String toString() { | |||
return "ParamAnnotationAccessVar(" + getType() + ")"; | |||
} | |||
private InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) { | |||
InstructionList il = new InstructionList(); | |||
Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS); | |||
Type jlString = BcelWorld.makeBcelType(UnresolvedType.JL_STRING); | |||
Type jlClassArray = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_CLASS_ARRAY); | |||
Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_ANNOTATION); | |||
Type jlaAnnotationArrayArray = BcelWorld.makeBcelType(UnresolvedType.makeArray(UnresolvedType.JAVA_LANG_ANNOTATION, 2)); | |||
Instruction pushConstant = fact.createConstant(new ObjectType(toType.getName())); | |||
if (kind == Shadow.MethodCall || kind == Shadow.MethodExecution || kind == Shadow.PreInitialization | |||
|| kind == Shadow.Initialization || kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution | |||
|| kind == Shadow.AdviceExecution || | |||
// annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD | |||
((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD)) { | |||
Type jlrMethod = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/reflect/Method;")); | |||
Type jlAnnotation = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/annotation/Annotation;")); | |||
Type[] paramTypes = BcelWorld.makeBcelTypes(member.getParameterTypes()); | |||
if (kind == Shadow.MethodCall | |||
|| kind == Shadow.MethodExecution | |||
|| kind == Shadow.AdviceExecution | |||
|| | |||
// annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD | |||
((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD) | |||
|| ((kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution) && member.getKind() == Member.METHOD)) { | |||
// Need to look at the cached annotation before going to fetch it again | |||
Field annotationCachingField = shadow.getEnclosingClass().getAnnotationCachingField(shadow, toType, isWithin); | |||
// Basic idea here is to check if the cached field is null, if it is then initialize it, otherwise use it | |||
il.append(fact.createGetStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation)); | |||
il.append(InstructionConstants.DUP); | |||
InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); | |||
il.append(ifNonNull); | |||
il.append(InstructionConstants.POP); | |||
il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); | |||
il.append(fact.createConstant(member.getName())); | |||
buildArray(il, fact, jlClass, paramTypes, 1); | |||
// OPTIMIZE cache result of getDeclaredMethod? | |||
il.append(fact.createInvoke("java/lang/Class", "getDeclaredMethod", jlrMethod, | |||
new Type[] { jlString, jlClassArray }, Constants.INVOKEVIRTUAL)); | |||
// il.append(pushConstant);// fact.createConstant(new ObjectType(toType.getName()))); | |||
il.append(fact.createInvoke("java/lang/reflect/Method", "getParameterAnnotations", jlaAnnotationArrayArray, null, Constants.INVOKEVIRTUAL)); | |||
il.append(fact.createConstant(parameterNumber)); | |||
il.append(InstructionConstants.AALOAD); | |||
il.append(fact.createConstant(annotationOnParameterNumber)); | |||
il.append(InstructionConstants.AALOAD); | |||
il.append(InstructionConstants.DUP); | |||
// Could loop over the set of annotations or we could 'guess' the right one? | |||
il.append(fact.createPutStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation)); | |||
InstructionHandle ifNullElse = il.append(InstructionConstants.NOP); | |||
ifNonNull.setTarget(ifNullElse); | |||
} else { // init/preinit/ctor-call/ctor-exec | |||
il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); | |||
buildArray(il, fact, jlClass, paramTypes, 1); | |||
Type jlrCtor = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_REFLECT_CONSTRUCTOR); | |||
// OPTIMIZE cache result of getDeclaredConstructor and getAnnotation? Might be able to use it again if someone else | |||
// needs the same annotations? | |||
il.append(fact.createInvoke("java/lang/Class", "getDeclaredConstructor", jlrCtor, new Type[] { jlClassArray }, | |||
Constants.INVOKEVIRTUAL)); | |||
il.append(pushConstant); | |||
il.append(fact.createInvoke("java/lang/reflect/Constructor", "getAnnotation", jlaAnnotation, | |||
new Type[] { jlClass }, Constants.INVOKEVIRTUAL)); | |||
} | |||
} else { | |||
throw new RuntimeException("Don't understand this kind " + kind); | |||
} | |||
il.append(Utility.createConversion(fact, jlaAnnotation, BcelWorld.makeBcelType(toType))); | |||
return il; | |||
} | |||
private void buildArray(InstructionList il, InstructionFactory fact, Type arrayElementType, Type[] arrayEntries, int dim) { | |||
il.append(fact.createConstant(Integer.valueOf(arrayEntries == null ? 0 : arrayEntries.length))); | |||
il.append(fact.createNewArray(arrayElementType, (short) dim)); | |||
if (arrayEntries == null) { | |||
return; | |||
} | |||
for (int i = 0; i < arrayEntries.length; i++) { | |||
il.append(InstructionFactory.createDup(1)); | |||
il.append(fact.createConstant(Integer.valueOf(i))); | |||
switch (arrayEntries[i].getType()) { | |||
case Constants.T_ARRAY: | |||
il.append(fact.createConstant(new ObjectType(arrayEntries[i].getSignature()))); // FIXME should be getName() and not | |||
// getSignature()? | |||
break; | |||
case Constants.T_BOOLEAN: | |||
il.append(fact.createGetStatic("java/lang/Boolean", "TYPE", arrayElementType)); | |||
break; | |||
case Constants.T_BYTE: | |||
il.append(fact.createGetStatic("java/lang/Byte", "TYPE", arrayElementType)); | |||
break; | |||
case Constants.T_CHAR: | |||
il.append(fact.createGetStatic("java/lang/Character", "TYPE", arrayElementType)); | |||
break; | |||
case Constants.T_INT: | |||
il.append(fact.createGetStatic("java/lang/Integer", "TYPE", arrayElementType)); | |||
break; | |||
case Constants.T_LONG: | |||
il.append(fact.createGetStatic("java/lang/Long", "TYPE", arrayElementType)); | |||
break; | |||
case Constants.T_DOUBLE: | |||
il.append(fact.createGetStatic("java/lang/Double", "TYPE", arrayElementType)); | |||
break; | |||
case Constants.T_FLOAT: | |||
il.append(fact.createGetStatic("java/lang/Float", "TYPE", arrayElementType)); | |||
break; | |||
case Constants.T_SHORT: | |||
il.append(fact.createGetStatic("java/lang/Short", "TYPE", arrayElementType)); | |||
break; | |||
default: | |||
il.append(fact.createConstant(arrayEntries[i])); | |||
} | |||
il.append(InstructionConstants.AASTORE); | |||
} | |||
} | |||
@Override | |||
public void appendLoad(InstructionList il, InstructionFactory fact) { | |||
il.append(createLoadInstructions(getType(), fact)); | |||
} | |||
@Override | |||
public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { | |||
il.append(createLoadInstructions(toType, fact)); | |||
} | |||
@Override | |||
public void insertLoad(InstructionList il, InstructionFactory fact) { | |||
il.insert(createLoadInstructions(getType(), fact)); | |||
} | |||
/** | |||
* Return an object that can access a particular field of this annotation. | |||
* | |||
* @param valueType The type from the annotation that is of interest | |||
* @param the formal name expressed in the pointcut, can be used to disambiguate | |||
* @return a variable that represents access to that annotation field | |||
*/ | |||
@Override | |||
public Var getAccessorForValue(ResolvedType valueType, String formalName) { | |||
throw new IllegalStateException("Not supported for parameter annotations"); | |||
} | |||
} |
@@ -26,6 +26,7 @@ import org.aspectj.apache.bcel.classfile.Attribute; | |||
import org.aspectj.apache.bcel.classfile.JavaClass; | |||
import org.aspectj.apache.bcel.classfile.LocalVariable; | |||
import org.aspectj.apache.bcel.classfile.LocalVariableTable; | |||
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; | |||
import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository; | |||
import org.aspectj.apache.bcel.util.Repository; | |||
import org.aspectj.weaver.AnnotationAJ; | |||
@@ -321,8 +322,7 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder { | |||
// here we really want both the runtime visible AND the class visible | |||
// annotations | |||
// so we bail out to Bcel and then chuck away the JavaClass so that we | |||
// don't hog | |||
// memory. | |||
// don't hog memory. | |||
try { | |||
JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass()); | |||
org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[][] anns = null; | |||
@@ -386,5 +386,72 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder { | |||
} | |||
return result; | |||
} | |||
public Object getParamAnnotation(Member onMember, int argsIndex, int paramAnnoIndex) { | |||
if (!(onMember instanceof AccessibleObject)) | |||
return null; | |||
// here we really want both the runtime visible AND the class visible | |||
// annotations so we bail out to Bcel and then chuck away the JavaClass so that we | |||
// don't hog memory. | |||
// try { | |||
// JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass()); | |||
// org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[][] anns = null; | |||
// if (onMember instanceof Method) { | |||
// org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember); | |||
// if (bcelMethod == null) { | |||
// // pr220430 | |||
// // System.err.println( | |||
// // "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '" | |||
// // + onMember.getName()+"' in class '"+jc.getClassName()+"'"); | |||
// } else { | |||
// anns = bcelMethod.getParameterAnnotations(); | |||
// } | |||
// } else if (onMember instanceof Constructor) { | |||
// org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember); | |||
// anns = bcelCons.getParameterAnnotations(); | |||
// } else if (onMember instanceof Field) { | |||
// // anns = null; | |||
// } | |||
// // the answer is cached and we don't want to hold on to memory | |||
// bcelRepository.clear(); | |||
// if (anns == null) | |||
// return null; | |||
// | |||
// if (argsIndex>=anns.length) { | |||
// return null; | |||
// } | |||
// AnnotationGen[] parameterAnnotationsOnParticularArg = anns[argsIndex]; | |||
// if (parameterAnnotationsOnParticularArg==null || paramAnnoIndex>=parameterAnnotationsOnParticularArg.length) { | |||
// return null; | |||
// } | |||
// AnnotationGen parameterAnnotation = parameterAnnotationsOnParticularArg[paramAnnoIndex]; | |||
//// if (parameterAnnotation.getTypeSignature().equals(ofType.getSignature())) { | |||
// return new BcelAnnotation(parameterAnnotation, world); | |||
//// } else { | |||
//// return null; | |||
//// } | |||
// } catch (ClassNotFoundException cnfEx) { | |||
// // just use reflection then | |||
// } | |||
// reflection... | |||
AccessibleObject ao = (AccessibleObject) onMember; | |||
Annotation[][] anns = null; | |||
if (onMember instanceof Method) { | |||
anns = ((Method) ao).getParameterAnnotations(); | |||
} else if (onMember instanceof Constructor) { | |||
anns = ((Constructor) ao).getParameterAnnotations(); | |||
} else if (onMember instanceof Field) { | |||
// anns = null; | |||
} | |||
if (anns == null || argsIndex>=anns.length) { | |||
return null; | |||
} | |||
Annotation[] parameterAnnotationsOnParticularArg = anns[argsIndex]; | |||
if (parameterAnnotationsOnParticularArg==null || paramAnnoIndex>=parameterAnnotationsOnParticularArg.length) { | |||
return null; | |||
} | |||
return parameterAnnotationsOnParticularArg[paramAnnoIndex]; | |||
} | |||
} |
@@ -0,0 +1,103 @@ | |||
/* ******************************************************************* | |||
* Copyright (c) 2016 Contributors. | |||
* All rights reserved. | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Eclipse Public License v1.0 | |||
* which accompanies this distribution and is available at | |||
* http://eclipse.org/legal/epl-v10.html | |||
* | |||
* Contributors: | |||
* Andy Clement Initial implementation | |||
* ******************************************************************/ | |||
package org.aspectj.weaver.reflect; | |||
import java.lang.annotation.Annotation; | |||
import java.util.Set; | |||
import org.aspectj.weaver.AnnotationAJ; | |||
import org.aspectj.weaver.ResolvedType; | |||
/** | |||
* An AnnotationAJ that wraps a java.lang.reflect Annotation. | |||
* | |||
* @author Andy Clement | |||
*/ | |||
public class ReflectiveAnnotationAJ implements AnnotationAJ { | |||
private Annotation anno; | |||
public ReflectiveAnnotationAJ(Annotation anno) { | |||
this.anno = anno; | |||
} | |||
public String getTypeSignature() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
public String getTypeName() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
public ResolvedType getType() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
public boolean allowedOnAnnotationType() { | |||
// TODO Auto-generated method stub | |||
return false; | |||
} | |||
public boolean allowedOnField() { | |||
// TODO Auto-generated method stub | |||
return false; | |||
} | |||
public boolean allowedOnRegularType() { | |||
// TODO Auto-generated method stub | |||
return false; | |||
} | |||
public Set<String> getTargets() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
public boolean hasNamedValue(String name) { | |||
// TODO Auto-generated method stub | |||
return false; | |||
} | |||
public boolean hasNameValuePair(String name, String value) { | |||
// TODO Auto-generated method stub | |||
return false; | |||
} | |||
public String getValidTargets() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
public String stringify() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
public boolean specifiesTarget() { | |||
// TODO Auto-generated method stub | |||
return false; | |||
} | |||
public boolean isRuntimeVisible() { | |||
// TODO Auto-generated method stub | |||
return false; | |||
} | |||
public String getStringFormOfValue(String name) { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
} |
@@ -11,6 +11,7 @@ | |||
* ******************************************************************/ | |||
package org.aspectj.weaver.tools; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.reflect.Method; | |||
@@ -63,6 +64,9 @@ public class Java15PointcutExpressionTest extends TestCase { | |||
private Method b; | |||
private Method c; | |||
private Method d; | |||
private Method e; | |||
private Method f; | |||
/** | |||
* Parse some expressions and ensure we capture the parameter annotations and parameter type annotations correctly. | |||
@@ -508,6 +512,43 @@ public class Java15PointcutExpressionTest extends TestCase { | |||
assertEquals("annotation on B",bAnnotation,jp2.getParameterBindings()[1].getBinding()); | |||
} | |||
public void testAtArgsWithParamBinding() throws Exception { | |||
// B class contains: public void e(@MyAnnotation A anA, B aB) {} | |||
PointcutParameter pp = parser.createPointcutParameter("a", MyAnnotation.class); | |||
PointcutExpression atArgs = parser.parsePointcutExpression("@args(a (*),..)",A.class,new PointcutParameter[] {pp}); | |||
ShadowMatch shadowMatch = atArgs.matchesMethodExecution(e); | |||
assertTrue(shadowMatch.alwaysMatches()); | |||
JoinPointMatch jpm = shadowMatch.matchesJoinPoint(new B(), new B(), new Object[] {new A(), new B()}); | |||
assertEquals(1,jpm.getParameterBindings().length); | |||
MyAnnotation expectedAnnotation = (MyAnnotation)B.class.getDeclaredMethod("e", A.class,B.class).getParameterAnnotations()[0][0]; | |||
Object boundAnnotation = jpm.getParameterBindings()[0].getBinding(); | |||
assertEquals(expectedAnnotation,boundAnnotation); | |||
pp = parser.createPointcutParameter("a", MyAnnotationWithValue.class); | |||
PointcutParameter pp2 = parser.createPointcutParameter("b", MyAnnotationWithValue.class); | |||
atArgs = parser.parsePointcutExpression("@args(a,b)",A.class,new PointcutParameter[] {pp,pp2}); | |||
shadowMatch = atArgs.matchesMethodExecution(f); | |||
assertTrue(shadowMatch.alwaysMatches()); | |||
jpm = shadowMatch.matchesJoinPoint(new B(), new B(), new Object[] {new A(), new B()}); | |||
assertEquals(2,jpm.getParameterBindings().length); | |||
MyAnnotationWithValue expectedAnnotation1 = (MyAnnotationWithValue)B.class.getDeclaredMethod("e", A.class,B.class).getParameterAnnotations()[0][0]; | |||
MyAnnotationWithValue expectedAnnotation2 = (MyAnnotationWithValue)B.class.getDeclaredMethod("e", A.class,B.class).getParameterAnnotations()[1][0]; | |||
Object boundAnnotation1 = jpm.getParameterBindings()[0].getBinding(); | |||
assertEquals(expectedAnnotation1,boundAnnotation1); | |||
Object boundAnnotation2 = jpm.getParameterBindings()[1].getBinding(); | |||
assertEquals(expectedAnnotation2,boundAnnotation1); | |||
// | |||
// atArgs = parser.parsePointcutExpression("@args(a,b)",A.class,new PointcutParameter[] {p1,p2}); | |||
// sMatch2 = atArgs.matchesMethodExecution(c); | |||
// assertTrue("maybe matches C",sMatch2.maybeMatches()); | |||
// jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[] {new B(),new B()}); | |||
// assertTrue("matches",jp2.matches()); | |||
// assertEquals(2,jp2.getParameterBindings().length); | |||
// assertEquals("annotation on B",bAnnotation,jp2.getParameterBindings()[0].getBinding()); | |||
// assertEquals("annotation on B",bAnnotation,jp2.getParameterBindings()[1].getBinding()); | |||
} | |||
public void testAtWithin() { | |||
PointcutExpression atWithin = parser.parsePointcutExpression("@within(org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); | |||
ShadowMatch sMatch1 = atWithin.matchesMethodExecution(a); | |||
@@ -670,11 +711,18 @@ public class Java15PointcutExpressionTest extends TestCase { | |||
b = B.class.getMethod("b"); | |||
c = B.class.getMethod("c",new Class[] {A.class,B.class}); | |||
d = B.class.getMethod("d",new Class[] {A.class,A.class}); | |||
e = B.class.getMethod("e", new Class[] {A.class, B.class}); | |||
f = B.class.getMethod("f", A.class, B.class); | |||
} | |||
@Retention(RetentionPolicy.RUNTIME) | |||
private @interface MyAnnotation {} | |||
@Retention(RetentionPolicy.RUNTIME) | |||
private @interface MyAnnotationWithValue { | |||
String value(); | |||
} | |||
private @interface MyClassFileRetentionAnnotation {} | |||
private static class A { | |||
@@ -687,6 +735,10 @@ public class Java15PointcutExpressionTest extends TestCase { | |||
public void c(A anA, B aB) {} | |||
public void d(A anA, A anotherA) {} | |||
public void e(@MyAnnotation A anA, B aB) {} | |||
public void f(@MyAnnotationWithValue("abc") A anA, @MyAnnotationWithValue("def") B aB) {} | |||
} | |||
private static class C { |