import java.lang.annotation.*; | |||||
interface Behavior { | |||||
String hello(); | |||||
} | |||||
aspect Trait { | |||||
// public String Behavior.name; | |||||
public String Behavior.hello() { | |||||
return "hello"; | |||||
} | |||||
} | |||||
public class Target implements Behavior { | |||||
public static aspect A { | |||||
// declare @field: * Target.name: @Tagged; // NO WORKY | |||||
declare @method: * Target.hello(..): @Tagged; // NO WORKY | |||||
} | |||||
public static void main(String []argv) throws Exception { | |||||
System.out.println(Target.class.getDeclaredMethod("hello").getDeclaredAnnotations()[0]); | |||||
} | |||||
} | |||||
@Retention(RetentionPolicy.RUNTIME) | |||||
@interface Tagged {} |
import java.lang.annotation.*; | |||||
interface Behavior { | |||||
String hello(); | |||||
} | |||||
aspect Trait { | |||||
// public String Behavior.name; | |||||
public String Behavior.hello() throws java.io.IOException { | |||||
return "hello"; | |||||
} | |||||
} | |||||
public class Target2 implements Behavior { | |||||
public static aspect A { | |||||
// declare @field: * Target2.name: @Tagged; // NO WORKY | |||||
declare @method: * Target2.hello(..): @Tagged; // NO WORKY | |||||
} | |||||
public static void main(String []argv) throws Exception { | |||||
System.out.println(Target2.class.getDeclaredMethod("hello").getDeclaredAnnotations()[0]); | |||||
} | |||||
} | |||||
@Retention(RetentionPolicy.RUNTIME) | |||||
@interface Tagged {} |
import java.lang.annotation.*; | |||||
interface Behavior { | |||||
String hello(); | |||||
} | |||||
aspect Trait { | |||||
@Wibble | |||||
public String Behavior.hello() throws java.io.IOException { | |||||
return "hello"; | |||||
} | |||||
} | |||||
public class Target3 implements Behavior { | |||||
public static aspect A { | |||||
declare @method: * Target3.hello(..): @Tagged; | |||||
} | |||||
public static void main(String []argv) throws Exception { | |||||
System.out.println(Target3.class.getDeclaredMethod("hello").getDeclaredAnnotations().length); | |||||
System.out.println(Target3.class.getDeclaredMethod("hello").getDeclaredAnnotations()[0]); | |||||
System.out.println(Target3.class.getDeclaredMethod("hello").getDeclaredAnnotations()[1]); | |||||
} | |||||
} | |||||
@Retention(RetentionPolicy.RUNTIME) @interface Tagged {} | |||||
@Retention(RetentionPolicy.RUNTIME) @interface Wibble {} |
import java.lang.annotation.*; | |||||
interface Behavior { | |||||
String hello(); | |||||
} | |||||
aspect Trait { | |||||
@Tagged(31) | |||||
public String Behavior.hello() throws java.io.IOException { | |||||
return "hello"; | |||||
} | |||||
} | |||||
public class Target4 implements Behavior { | |||||
public static aspect A { | |||||
declare @method: * Target4.hello(..): @Tagged; | |||||
} | |||||
public static void main(String []argv) throws Exception { | |||||
System.out.println(Target4.class.getDeclaredMethod("hello").getDeclaredAnnotations().length); | |||||
System.out.println(Target4.class.getDeclaredMethod("hello").getDeclaredAnnotations()[0]); | |||||
} | |||||
} | |||||
@Retention(RetentionPolicy.RUNTIME) @interface Tagged { int value() default 42;} |
*/ | */ | ||||
public class Ajc174Tests extends org.aspectj.testing.XMLBasedAjcTestCase { | public class Ajc174Tests extends org.aspectj.testing.XMLBasedAjcTestCase { | ||||
public void testAnnotatedItd_418129() throws Exception { | |||||
runTest("annotated itd"); | |||||
} | |||||
public void testAnnotatedItd_418129_2() throws Exception { | |||||
runTest("annotated itd 2"); | |||||
} | |||||
public void testAnnotatedItd_418129_3() throws Exception { | |||||
runTest("annotated itd 3"); | |||||
} | |||||
public void testAnnotatedItd_418129_4() throws Exception { | |||||
runTest("annotated itd 4"); | |||||
} | |||||
public void testSuperItdCtor_413378() throws Exception { | public void testSuperItdCtor_413378() throws Exception { | ||||
runTest("super itd ctor"); | runTest("super itd ctor"); | ||||
} | } |
<suite> | <suite> | ||||
<ajc-test dir="bugs174/pr418129" title="annotated itd"> | |||||
<compile files="Target.java" options="-1.5 -showWeaveInfo"> | |||||
<message kind="weave" text="Type 'Behavior' (Target.java) has intertyped method from 'Trait' (Target.java:'java.lang.String Behavior.hello()')"/> | |||||
<message kind="weave" text="Type 'Target' (Target.java) has intertyped method from 'Trait' (Target.java:'java.lang.String Behavior.hello()')"/> | |||||
<message kind="weave" text="'public java.lang.String Target.hello()' (Target.java) is annotated with @Tagged method annotation from 'Target$A' (Target.java:18)"/> | |||||
</compile> | |||||
<run class="Target"> | |||||
<stdout> | |||||
<line text="@Tagged()"/> | |||||
</stdout> | |||||
</run> | |||||
</ajc-test> | |||||
<!-- declared with throws exception --> | |||||
<ajc-test dir="bugs174/pr418129" title="annotated itd 2"> | |||||
<compile files="Target2.java" options="-1.5 -showWeaveInfo"> | |||||
<message kind="weave" text="Type 'Behavior' (Target2.java) has intertyped method from 'Trait' (Target2.java:'java.lang.String Behavior.hello()')"/> | |||||
<message kind="weave" text="Type 'Target2' (Target2.java) has intertyped method from 'Trait' (Target2.java:'java.lang.String Behavior.hello()')"/> | |||||
<message kind="weave" text="'public java.lang.String Target2.hello()' (Target2.java) is annotated with @Tagged method annotation from 'Target2$A' (Target2.java:18)"/> | |||||
</compile> | |||||
<run class="Target2"> | |||||
<stdout> | |||||
<line text="@Tagged()"/> | |||||
</stdout> | |||||
</run> | |||||
</ajc-test> | |||||
<!-- already annotated with another annotation --> | |||||
<ajc-test dir="bugs174/pr418129" title="annotated itd 3"> | |||||
<compile files="Target3.java" options="-1.5 -showWeaveInfo"> | |||||
<message kind="weave" text="Type 'Behavior' (Target3.java) has intertyped method from 'Trait' (Target3.java:'java.lang.String Behavior.hello()')"/> | |||||
<message kind="weave" text="Type 'Target3' (Target3.java) has intertyped method from 'Trait' (Target3.java:'java.lang.String Behavior.hello()')"/> | |||||
<message kind="weave" text="'public java.lang.String Target3.hello()' (Target3.java) is annotated with @Tagged method annotation from 'Target3$A' (Target3.java:16)"/> | |||||
</compile> | |||||
<run class="Target3"> | |||||
<stdout> | |||||
<line text="2"/> | |||||
<line text="@Wibble()"/> | |||||
<line text="@Tagged()"/> | |||||
</stdout> | |||||
</run> | |||||
</ajc-test> | |||||
<!-- already annotated with the same annotation --> | |||||
<ajc-test dir="bugs174/pr418129" title="annotated itd 4"> | |||||
<compile files="Target4.java" options="-1.5 -showWeaveInfo"> | |||||
<message kind="weave" text="Type 'Behavior' (Target4.java) has intertyped method from 'Trait' (Target4.java:'java.lang.String Behavior.hello()')"/> | |||||
<message kind="weave" text="Type 'Target4' (Target4.java) has intertyped method from 'Trait' (Target4.java:'java.lang.String Behavior.hello()')"/> | |||||
<!-- warning turned off as it gets confusing - when the itd on the interface is hit by a deca --> | |||||
<!-- | |||||
<message kind="warning" text="java.lang.String Target4.hello() - already has an annotation of type Tagged, cannot add a second instance [Xlint:elementAlreadyAnnotated]"/> | |||||
--> | |||||
</compile> | |||||
<run class="Target4"> | |||||
<stdout> | |||||
<line text="1"/> | |||||
<line text="@Tagged(value=31)"/> | |||||
</stdout> | |||||
</run> | |||||
</ajc-test> | |||||
<ajc-test dir="bugs174/pr413378" title="super itd ctor"> | <ajc-test dir="bugs174/pr413378" title="super itd ctor"> | ||||
<compile files="Code.java" options="-1.5 -showWeaveInfo"> | <compile files="Code.java" options="-1.5 -showWeaveInfo"> | ||||
<message kind="weave" text="Type 'Child' (Code.java) has intertyped constructor from 'MyTest' (Code.java:'void Child.<init>(java.lang.String, int)')"/> | <message kind="weave" text="Type 'Child' (Code.java) has intertyped constructor from 'MyTest' (Code.java:'void Child.<init>(java.lang.String, int)')"/> |
import org.aspectj.weaver.IClassWeaver; | import org.aspectj.weaver.IClassWeaver; | ||||
import org.aspectj.weaver.IntMap; | import org.aspectj.weaver.IntMap; | ||||
import org.aspectj.weaver.Member; | import org.aspectj.weaver.Member; | ||||
import org.aspectj.weaver.MemberKind; | |||||
import org.aspectj.weaver.MissingResolvedTypeWithKnownSignature; | import org.aspectj.weaver.MissingResolvedTypeWithKnownSignature; | ||||
import org.aspectj.weaver.NameMangler; | import org.aspectj.weaver.NameMangler; | ||||
import org.aspectj.weaver.NewConstructorTypeMunger; | import org.aspectj.weaver.NewConstructorTypeMunger; | ||||
// finally, if we changed, we add in the introduced methods. | // finally, if we changed, we add in the introduced methods. | ||||
if (isChanged) { | if (isChanged) { | ||||
clazz.getOrCreateWeaverStateInfo(inReweavableMode); | clazz.getOrCreateWeaverStateInfo(inReweavableMode); | ||||
weaveInAddedMethods(); // FIXME asc are these potentially affected | |||||
// by declare annotation? | |||||
weaveInAddedMethods(); | |||||
} | } | ||||
if (inReweavableMode) { | if (inReweavableMode) { | ||||
isChanged = weaveAtMethodOnITDSRepeatedly(allDecams, itdMethodsCtors, reportedProblems); | isChanged = weaveAtMethodOnITDSRepeatedly(allDecams, itdMethodsCtors, reportedProblems); | ||||
} | } | ||||
// deal with all the other methods... | |||||
List<LazyMethodGen> members = clazz.getMethodGens(); | |||||
List<DeclareAnnotation> decaMs = getMatchingSubset(allDecams, clazz.getType()); | List<DeclareAnnotation> decaMs = getMatchingSubset(allDecams, clazz.getType()); | ||||
if (decaMs.isEmpty()) { | if (decaMs.isEmpty()) { | ||||
return false; // nothing to do | return false; // nothing to do | ||||
} | } | ||||
Set<DeclareAnnotation> unusedDecams = new HashSet<DeclareAnnotation>(); | |||||
unusedDecams.addAll(decaMs); | |||||
// These methods may have been targeted with declare annotation. Example: ITD on an interface | |||||
// where the top most implementor gets a real method. The top most implementor method | |||||
// is an 'addedLazyMethodGen' | |||||
if (addedLazyMethodGens!=null) { | |||||
for (LazyMethodGen method: addedLazyMethodGens) { | |||||
// They have no resolvedmember of their own, conjure one up for matching purposes | |||||
ResolvedMember resolvedmember = | |||||
new ResolvedMemberImpl(ResolvedMember.METHOD,method.getEnclosingClass().getType(),method.getAccessFlags(), | |||||
BcelWorld.fromBcel(method.getReturnType()),method.getName(), | |||||
BcelWorld.fromBcel(method.getArgumentTypes()),UnresolvedType.forNames(method.getDeclaredExceptions())); | |||||
resolvedmember.setAnnotationTypes(method.getAnnotationTypes()); | |||||
resolvedmember.setAnnotations(method.getAnnotations()); | |||||
List<DeclareAnnotation> worthRetrying = new ArrayList<DeclareAnnotation>(); | |||||
boolean modificationOccured = false; | |||||
for (DeclareAnnotation decam: decaMs) { | |||||
if (decam.matches(resolvedmember, world)) { | |||||
if (doesAlreadyHaveAnnotation(resolvedmember, decam, reportedProblems,false)) { | |||||
// remove the declare @method since don't want an error when the annotation is already there | |||||
unusedDecams.remove(decam); | |||||
continue; | |||||
} | |||||
AnnotationGen a = ((BcelAnnotation) decam.getAnnotation()).getBcelAnnotation(); | |||||
// create copy to get the annotation type into the right constant pool | |||||
AnnotationAJ aj = new BcelAnnotation(new AnnotationGen(a, clazz.getConstantPool(), true),world); | |||||
method.addAnnotation(aj); | |||||
resolvedmember.addAnnotation(decam.getAnnotation()); | |||||
AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decam.getSourceLocation(), | |||||
clazz.getName(), resolvedmember, world.getModelAsAsmManager()); | |||||
reportMethodCtorWeavingMessage(clazz, resolvedmember, decam, method.getDeclarationLineNumber()); | |||||
isChanged = true; | |||||
modificationOccured = true; | |||||
unusedDecams.remove(decam); | |||||
} else if (!decam.isStarredAnnotationPattern()) { | |||||
// an annotation is specified that might be put on by a subsequent decaf | |||||
worthRetrying.add(decam); | |||||
} | |||||
} | |||||
// Multiple secondary passes | |||||
while (!worthRetrying.isEmpty() && modificationOccured) { | |||||
modificationOccured = false; | |||||
// lets have another go | |||||
List<DeclareAnnotation> forRemoval = new ArrayList<DeclareAnnotation>(); | |||||
for (DeclareAnnotation decam : worthRetrying) { | |||||
if (decam.matches(resolvedmember, world)) { | |||||
if (doesAlreadyHaveAnnotation(resolvedmember, decam, reportedProblems,false)) { | |||||
// remove the declare @method since don't | |||||
// want an error when | |||||
// the annotation is already there | |||||
unusedDecams.remove(decam); | |||||
continue; // skip this one... | |||||
} | |||||
AnnotationGen a = ((BcelAnnotation) decam.getAnnotation()).getBcelAnnotation(); | |||||
// create copy to get the annotation type into the right constant pool | |||||
AnnotationAJ aj = new BcelAnnotation(new AnnotationGen(a, clazz.getConstantPool(), true),world); | |||||
method.addAnnotation(aj); | |||||
resolvedmember.addAnnotation(decam.getAnnotation()); | |||||
AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decam.getSourceLocation(), | |||||
clazz.getName(), resolvedmember, world.getModelAsAsmManager());// getMethod()); | |||||
isChanged = true; | |||||
modificationOccured = true; | |||||
forRemoval.add(decam); | |||||
unusedDecams.remove(decam); | |||||
} | |||||
} | |||||
worthRetrying.removeAll(forRemoval); | |||||
} | |||||
} | |||||
} | |||||
// deal with all the other methods... | |||||
List<LazyMethodGen> members = clazz.getMethodGens(); | |||||
if (!members.isEmpty()) { | if (!members.isEmpty()) { | ||||
Set<DeclareAnnotation> unusedDecams = new HashSet<DeclareAnnotation>(); | |||||
unusedDecams.addAll(decaMs); | |||||
for (int memberCounter = 0; memberCounter < members.size(); memberCounter++) { | for (int memberCounter = 0; memberCounter < members.size(); memberCounter++) { | ||||
LazyMethodGen mg = members.get(memberCounter); | LazyMethodGen mg = members.get(memberCounter); | ||||
if (!mg.getName().startsWith(NameMangler.PREFIX)) { | if (!mg.getName().startsWith(NameMangler.PREFIX)) { | ||||
for (DeclareAnnotation decaM : decaMs) { | for (DeclareAnnotation decaM : decaMs) { | ||||
if (decaM.matches(mg.getMemberView(), world)) { | if (decaM.matches(mg.getMemberView(), world)) { | ||||
if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems)) { | |||||
if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems,true)) { | |||||
// remove the declare @method since don't want | // remove the declare @method since don't want | ||||
// an error when the annotation is already there | // an error when the annotation is already there | ||||
unusedDecams.remove(decaM); | unusedDecams.remove(decaM); | ||||
List<DeclareAnnotation> forRemoval = new ArrayList<DeclareAnnotation>(); | List<DeclareAnnotation> forRemoval = new ArrayList<DeclareAnnotation>(); | ||||
for (DeclareAnnotation decaM : worthRetrying) { | for (DeclareAnnotation decaM : worthRetrying) { | ||||
if (decaM.matches(mg.getMemberView(), world)) { | if (decaM.matches(mg.getMemberView(), world)) { | ||||
if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems)) { | |||||
if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems,true)) { | |||||
// remove the declare @method since don't | // remove the declare @method since don't | ||||
// want an error when | // want an error when | ||||
// the annotation is already there | // the annotation is already there | ||||
unusedDecafs.remove(decaf); | unusedDecafs.remove(decaf); | ||||
} else { | } else { | ||||
if (!dontAddTwice(decaf, dontAddMeTwice)) { | if (!dontAddTwice(decaf, dontAddMeTwice)) { | ||||
if (doesAlreadyHaveAnnotation(field, decaf, reportedProblems)) { | |||||
if (doesAlreadyHaveAnnotation(field, decaf, reportedProblems,true )) { | |||||
// remove the declare @field since don't want an error when the annotation is already there | // remove the declare @field since don't want an error when the annotation is already there | ||||
unusedDecafs.remove(decaf); | unusedDecafs.remove(decaf); | ||||
continue; | continue; | ||||
} else { | } else { | ||||
// below code is for recursive things | // below code is for recursive things | ||||
unusedDecafs.remove(decaF); | unusedDecafs.remove(decaF); | ||||
if (doesAlreadyHaveAnnotation(field, decaF, reportedProblems)) { | |||||
if (doesAlreadyHaveAnnotation(field, decaF, reportedProblems,true)) { | |||||
continue; | continue; | ||||
} | } | ||||
field.addAnnotation(decaF.getAnnotation()); | field.addAnnotation(decaF.getAnnotation()); | ||||
/** | /** | ||||
* Check if a resolved member (field/method/ctor) already has an annotation, if it does then put out a warning and return true | * Check if a resolved member (field/method/ctor) already has an annotation, if it does then put out a warning and return true | ||||
*/ | */ | ||||
private boolean doesAlreadyHaveAnnotation(ResolvedMember rm, DeclareAnnotation deca, List<Integer> reportedProblems) { | |||||
private boolean doesAlreadyHaveAnnotation(ResolvedMember rm, DeclareAnnotation deca, List<Integer> reportedProblems, boolean reportError) { | |||||
if (rm.hasAnnotation(deca.getAnnotationType())) { | if (rm.hasAnnotation(deca.getAnnotationType())) { | ||||
if (world.getLint().elementAlreadyAnnotated.isEnabled()) { | |||||
if (reportError && world.getLint().elementAlreadyAnnotated.isEnabled()) { | |||||
Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); | Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); | ||||
if (!reportedProblems.contains(uniqueID)) { | if (!reportedProblems.contains(uniqueID)) { | ||||
reportedProblems.add(uniqueID); | reportedProblems.add(uniqueID); |
return ret; | return ret; | ||||
} | } | ||||
public static Type[] makeBcelTypes(String[] types) { | |||||
if (types == null || types.length==0 ) { | |||||
return null; | |||||
} | |||||
Type[] ret = new Type[types.length]; | |||||
for (int i=0, len=types.length; i<len; i++) { | |||||
ret[i] = makeBcelType(types[i]); | |||||
} | |||||
return ret; | |||||
} | |||||
public static Type makeBcelType(String type) { | |||||
return Type.getType(type); | |||||
} | |||||
static String[] makeBcelTypesAsClassNames(UnresolvedType[] types) { | static String[] makeBcelTypesAsClassNames(UnresolvedType[] types) { | ||||
String[] ret = new String[types.length]; | String[] ret = new String[types.length]; | ||||
for (int i = 0, len = types.length; i < len; i++) { | for (int i = 0, len = types.length; i < len; i++) { | ||||
public void demote(ResolvedType type) { | public void demote(ResolvedType type) { | ||||
typeMap.demote(type); | typeMap.demote(type); | ||||
} | } | ||||
} | } |
List<BcelShadow> matchedShadows; | List<BcelShadow> matchedShadows; | ||||
// Used for interface introduction - this is the type of the interface the method is technically on | // Used for interface introduction - this is the type of the interface the method is technically on | ||||
public ResolvedType definingType = null; | public ResolvedType definingType = null; | ||||
static class LightweightBcelMethod extends BcelMethod { | |||||
LightweightBcelMethod(BcelObjectType declaringType, Method method) { | |||||
super(declaringType, method); | |||||
// TODO Auto-generated constructor stub | |||||
} | |||||
} | |||||
public LazyMethodGen(int modifiers, Type returnType, String name, Type[] paramTypes, String[] declaredExceptions, | public LazyMethodGen(int modifiers, Type returnType, String name, Type[] paramTypes, String[] declaredExceptions, | ||||
LazyClassGen enclosingClass) { | LazyClassGen enclosingClass) { | ||||
memberView.addParameterAnnotation(parameterNumber, anno); | memberView.addParameterAnnotation(parameterNumber, anno); | ||||
} | } | ||||
} | } | ||||
public ResolvedType[] getAnnotationTypes() { | |||||
initialize(); | |||||
if (memberView == null && newAnnotations!=null && newAnnotations.size()!=0) { | |||||
// TODO Ignoring removed annotations for now | |||||
ResolvedType[] annotationTypes = new ResolvedType[newAnnotations.size()]; | |||||
for (int a=0,len=newAnnotations.size();a<len;a++) { | |||||
annotationTypes[a] = newAnnotations.get(a).getType(); | |||||
} | |||||
return annotationTypes; | |||||
} | |||||
return null; | |||||
} | |||||
public AnnotationAJ[] getAnnotations() { | |||||
initialize(); | |||||
if (memberView == null && newAnnotations!=null && newAnnotations.size()!=0) { | |||||
return newAnnotations.toArray(new AnnotationAJ[newAnnotations.size()]); | |||||
} | |||||
return null; | |||||
} | |||||
public boolean hasAnnotation(UnresolvedType annotationType) { | public boolean hasAnnotation(UnresolvedType annotationType) { | ||||
initialize(); | initialize(); |