diff options
author | Andy Clement <andrew.clement@gmail.com> | 2012-04-02 14:15:08 -0700 |
---|---|---|
committer | Andy Clement <andrew.clement@gmail.com> | 2012-04-02 14:15:08 -0700 |
commit | 096b004fda4d21cca0e1ee4c776e5824715d0ecd (patch) | |
tree | 1b56bf3e3ba31d909df8822f9ef7093166916c19 | |
parent | f85631fd2fb2e0f3213abb9c5a7cd86eec2c9ab5 (diff) | |
download | aspectj-096b004fda4d21cca0e1ee4c776e5824715d0ecd.tar.gz aspectj-096b004fda4d21cca0e1ee4c776e5824715d0ecd.zip |
375881
31 files changed, 843 insertions, 9 deletions
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java b/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java index bab276699..bc6695e3e 100644 --- a/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java +++ b/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java @@ -21,8 +21,10 @@ 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; @@ -36,6 +38,7 @@ 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; @@ -50,7 +53,14 @@ 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.NamePattern; +import org.aspectj.weaver.patterns.PatternParser; import org.aspectj.weaver.patterns.PerClause; import org.aspectj.weaver.patterns.PerSingleton; @@ -134,6 +144,11 @@ public class ConcreteAspectCodeGen { 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) { @@ -471,6 +486,13 @@ public class ConcreteAspectCodeGen { 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); @@ -492,6 +514,275 @@ public class ConcreteAspectCodeGen { } /** + * The DeclareAnnotation object encapsulates an method/field/type descriptor and an annotation. This uses a DeclareAnnotation object + * captured from the XML (something like '<declare-annotation field="* field1(..)" annotation="@Annot(a='a',fred=false,'abc')"/>') + * 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 + } + if (da.declareAnnotationKind==DeclareAnnotationKind.Method || da.declareAnnotationKind==DeclareAnnotationKind.Field) { + String declareName = new StringBuilder("ajc$declare_at_").append(da.declareAnnotationKind==DeclareAnnotationKind.Method?"method":"field").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); + + ITokenSource tokenSource = BasicTokenSource.makeTokenSource(da.pattern,null); + PatternParser pp = new PatternParser(tokenSource); + ISignaturePattern isp = (da.declareAnnotationKind==DeclareAnnotationKind.Method?pp.parseCompoundMethodOrConstructorSignaturePattern(true):pp.parseCompoundFieldSignaturePattern()); + DeclareAnnotation deca = new DeclareAnnotation(da.declareAnnotationKind==DeclareAnnotationKind.Method?DeclareAnnotation.AT_METHOD:DeclareAnnotation.AT_FIELD, isp); + 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<String> values = new ArrayList<String>(); + int pos = paren+1; + int depth = 0; + int len = annotationString.length(); + int start = pos; + while (pos<len) { + char ch = annotationString.charAt(pos); + if (ch==')' && depth==0) { + break; // reached the end + } + if (ch=='(' || ch=='[') { + depth++; + } else if (ch==')' || ch==']') { + depth--; + } + if (ch==',' && depth==0) { + // found a comma at the right level to end a name/value pair + values.add(annotationString.substring(start,pos).trim()); + start=pos+1; + } + pos++; + } + if (start != pos) { + // there is a last bit to add + values.add(annotationString.substring(start,pos).trim()); + } + AnnotationGen aaj = buildBaseAnnotationType(cp,world,name); + if (aaj==null) { + return null; // a check failed + } + String typename = aaj.getTypeName(); + ResolvedType type = UnresolvedType.forName(typename).resolve(world); // shame it isn't retrievable from the anno + ResolvedMember[] rms = type.getDeclaredMethods(); + // Add the values + for (String value: values) { + int equalsIndex = value.indexOf("="); + String key = "value"; + if (value.charAt(0)!='\"' && equalsIndex!=-1) { + key = value.substring(0,equalsIndex).trim(); + value = value.substring(equalsIndex+1).trim(); + } + boolean keyIsOk = false; + for (int m=0;m<rms.length;m++) { + NameValuePair nvp = null; + if (rms[m].getName().equals(key)) { + // found it! + keyIsOk=true; + UnresolvedType rt = rms[m].getReturnType(); + if (rt.isPrimitiveType()) { + switch (rt.getSignature().charAt(0)) { + case 'J': // long + try { + long longValue = Long.parseLong(value); + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_LONG,cp,longValue),cp); + } catch (NumberFormatException nfe) { + reportError("unable to interpret annotation value '"+value+"' as a long"); + return null; + } + break; + case 'S': // short + try { + short shortValue = Short.parseShort(value); + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_SHORT,cp,shortValue),cp); + } catch (NumberFormatException nfe) { + reportError("unable to interpret annotation value '"+value+"' as a short"); + return null; + } + break; + case 'F': // float + try { + float floatValue = Float.parseFloat(value); + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_FLOAT,cp,floatValue),cp); + } catch (NumberFormatException nfe) { + reportError("unable to interpret annotation value '"+value+"' as a float"); + return null; + } + break; + case 'D': // double + try { + double doubleValue = Double.parseDouble(value); + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_DOUBLE,cp,doubleValue),cp); + } catch (NumberFormatException nfe) { + reportError("unable to interpret annotation value '"+value+"' as a double"); + return null; + } + break; + case 'I': // integer + try { + int intValue = Integer.parseInt(value); + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_INT,cp,intValue),cp); + } catch (NumberFormatException nfe) { + reportError("unable to interpret annotation value '"+value+"' as an integer"); + return null; + } + break; + case 'B': // byte + try { + byte byteValue = Byte.parseByte(value); + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_BYTE,cp,byteValue),cp); + } catch (NumberFormatException nfe) { + reportError("unable to interpret annotation value '"+value+"' as a byte"); + return null; + } + break; + case 'C': // char + if (value.length()<2) { + reportError("unable to interpret annotation value '"+value+"' as a char"); + return null; + } + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_CHAR,cp,value.charAt(1)),cp); + break; + case 'Z': // boolean + try { + boolean booleanValue = Boolean.parseBoolean(value); + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_BOOLEAN,cp,booleanValue),cp); + } catch (NumberFormatException nfe) { + reportError("unable to interpret annotation value '"+value+"' as a boolean"); + return null; + } + break; + default: + reportError("not yet supporting XML setting of annotation values of type "+rt.getName()); + return null; + } + } else if (UnresolvedType.JL_STRING.equals(rt)) { + if (value.length()<2) { + reportError("Invalid string value specified in annotation string: "+annotationString); + return null; + } + value = value.substring(1,value.length()-1); // trim the quotes off + nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.STRING,cp,value),cp); + } else if (UnresolvedType.JL_CLASS.equals(rt)) { + // format of class string: + // Foo.class + // java.lang.Foo.class + if (value.length()<6) { + reportError("Not a well formed class value for an annotation '"+value+"'"); + return null; + } + String clazz = value.substring(0,value.length()-6); + boolean qualified = clazz.indexOf(".")!=-1; + if (!qualified) { + // if not qualified, have to assume java.lang + clazz = "java.lang."+clazz; + } + nvp = new NameValuePair(key,new ClassElementValue(new ObjectType(clazz),cp),cp); + } + } + if (nvp!=null) { + aaj.addElementNameValuePair(nvp); + } + } + if (!keyIsOk) { + reportError("annotation @"+typename+" does not have a value named "+key); + return null; + } + } + return aaj; + } + } + + private AnnotationGen buildBaseAnnotationType(ConstantPool cp,World world, String typename) { + String annoname = typename; + if (annoname.startsWith("@")) { + annoname= annoname.substring(1); + } + ResolvedType annotationType = UnresolvedType.forName(annoname).resolve(world); + if (!annotationType.isAnnotation()) { + reportError("declare is not specifying an annotation type :"+typename); + return null; + } + if (!annotationType.isAnnotationWithRuntimeRetention()) { + reportError("declare is using an annotation type that does not have runtime retention: "+typename); + return null; + } + List<NameValuePair> elems = new ArrayList<NameValuePair>(); + 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. */ diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java index 37bc7eda1..9e2a62618 100644 --- a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java @@ -80,7 +80,7 @@ public class BasicTokenSource implements ITokenSource { ////////////////////////////////////////////////////// // Convenience, maybe just for testing - static ITokenSource makeTokenSource(String input, ISourceContext context) { + public static ITokenSource makeTokenSource(String input, ISourceContext context) { char[] chars = input.toCharArray(); int i = 0; diff --git a/tests/bugs170/xmldefs/Anno.class b/tests/bugs170/xmldefs/Anno.class Binary files differnew file mode 100644 index 000000000..6e378d760 --- /dev/null +++ b/tests/bugs170/xmldefs/Anno.class diff --git a/tests/bugs170/xmldefs/Anno.java b/tests/bugs170/xmldefs/Anno.java new file mode 100644 index 000000000..7bb1cb2aa --- /dev/null +++ b/tests/bugs170/xmldefs/Anno.java @@ -0,0 +1,5 @@ +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Anno { +} diff --git a/tests/bugs170/xmldefs/Anno2.java b/tests/bugs170/xmldefs/Anno2.java new file mode 100644 index 000000000..f5dfcb99f --- /dev/null +++ b/tests/bugs170/xmldefs/Anno2.java @@ -0,0 +1,5 @@ +import java.lang.annotation.*; + +// deliberately wrong retention +public @interface Anno2 { +} diff --git a/tests/bugs170/xmldefs/Anno4.java b/tests/bugs170/xmldefs/Anno4.java new file mode 100644 index 000000000..ebff22092 --- /dev/null +++ b/tests/bugs170/xmldefs/Anno4.java @@ -0,0 +1,25 @@ +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoString { + String value() default "xyz"; + String sss() default "xyz"; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoLong { + long value() default 111L; + long jjj() default 111L; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoBoolean { + boolean value() default false; + boolean zzz() default false; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoClass { + Class value() default String.class; + Class ccc() default String.class; +} diff --git a/tests/bugs170/xmldefs/Anno5.java b/tests/bugs170/xmldefs/Anno5.java new file mode 100644 index 000000000..5906bcaa1 --- /dev/null +++ b/tests/bugs170/xmldefs/Anno5.java @@ -0,0 +1,37 @@ +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoShort { + short value() default 3; + short sss() default 3; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoDouble { + double value() default 3.0d; + double ddd() default 3.0d; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoFloat { + float value() default 4.0f; + float fff() default 4.0f; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoChar { + char value() default 'a'; + char ccc() default 'a'; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoByte { + byte value() default 66; + byte bbb() default 66; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface AnnoInt { + int value() default 111; + int iii() default 111; +} diff --git a/tests/bugs170/xmldefs/Anno6.java b/tests/bugs170/xmldefs/Anno6.java new file mode 100644 index 000000000..18d8f544b --- /dev/null +++ b/tests/bugs170/xmldefs/Anno6.java @@ -0,0 +1,9 @@ +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@interface Annot { + char a(); + boolean fred(); + String value(); +} + diff --git a/tests/bugs170/xmldefs/AnnoBoolean.class b/tests/bugs170/xmldefs/AnnoBoolean.class Binary files differnew file mode 100644 index 000000000..33b76156a --- /dev/null +++ b/tests/bugs170/xmldefs/AnnoBoolean.class diff --git a/tests/bugs170/xmldefs/AnnoClass.class b/tests/bugs170/xmldefs/AnnoClass.class Binary files differnew file mode 100644 index 000000000..4071d56d6 --- /dev/null +++ b/tests/bugs170/xmldefs/AnnoClass.class diff --git a/tests/bugs170/xmldefs/AnnoLong.class b/tests/bugs170/xmldefs/AnnoLong.class Binary files differnew file mode 100644 index 000000000..a7990fbad --- /dev/null +++ b/tests/bugs170/xmldefs/AnnoLong.class diff --git a/tests/bugs170/xmldefs/AnnoString.class b/tests/bugs170/xmldefs/AnnoString.class Binary files differnew file mode 100644 index 000000000..ba0e8eaf7 --- /dev/null +++ b/tests/bugs170/xmldefs/AnnoString.class diff --git a/tests/bugs170/xmldefs/Foo.class b/tests/bugs170/xmldefs/Foo.class Binary files differnew file mode 100644 index 000000000..eaa12c965 --- /dev/null +++ b/tests/bugs170/xmldefs/Foo.class diff --git a/tests/bugs170/xmldefs/Foo.java b/tests/bugs170/xmldefs/Foo.java new file mode 100644 index 000000000..2edbdabfd --- /dev/null +++ b/tests/bugs170/xmldefs/Foo.java @@ -0,0 +1,3 @@ +public aspect Foo { + declare @method: * sayHello(..): @Anno; +} diff --git a/tests/bugs170/xmldefs/Hello.java b/tests/bugs170/xmldefs/Hello.java new file mode 100644 index 000000000..fdf29eab3 --- /dev/null +++ b/tests/bugs170/xmldefs/Hello.java @@ -0,0 +1,36 @@ +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +public class Hello { + + public static void main(String[] args) { + sayHello(); + printAnnos("sayHello"); + } + + public static void sayHello() { + System.out.println("Hello"); + sayWorld(); + } + + public static int sayWorld() { + System.out.println("World"); + return 0; + } + + public static void printAnnos(String methodname) { + try { + Method m = Hello.class.getDeclaredMethod(methodname); + Annotation[] annos = m.getAnnotations(); + System.out.println("Annotations on "+methodname+"? "+(annos!=null && annos.length!=0)); + if (annos!=null && annos.length>0) { + System.out.println("Annotation count is "+annos.length); + for (Annotation anno: annos) { + System.out.println(anno); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/tests/bugs170/xmldefs/Hello2.java b/tests/bugs170/xmldefs/Hello2.java new file mode 100644 index 000000000..9fa6a4a9a --- /dev/null +++ b/tests/bugs170/xmldefs/Hello2.java @@ -0,0 +1,27 @@ +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +public class Hello2 { + + public static int i; + + public static void main(String[] args) { + printAnnos("i"); + } + + public static void printAnnos(String fieldname) { + try { + Field m = Hello2.class.getDeclaredField(fieldname); + Annotation[] annos = m.getAnnotations(); + System.out.println("Annotations on "+fieldname+"? "+(annos!=null && annos.length!=0)); + if (annos!=null && annos.length>0) { + System.out.println("Annotation count is "+annos.length); + for (Annotation anno: annos) { + System.out.println(anno); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/tests/bugs170/xmldefs/Hello4.java b/tests/bugs170/xmldefs/Hello4.java new file mode 100644 index 000000000..7f7c6183b --- /dev/null +++ b/tests/bugs170/xmldefs/Hello4.java @@ -0,0 +1,42 @@ +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.*; +public class Hello4 { + + public static int field1; + public static int field2; + + public static void main(String[] args) { + printAnnos("field1"); + printAnnos("field2"); + } + + public static void printAnnos(String fieldname) { + try { + Field m = Hello4.class.getDeclaredField(fieldname); + Annotation[] annos = m.getAnnotations(); + System.out.println("Annotations on "+fieldname+"? "+(annos!=null && annos.length!=0)); + if (annos!=null && annos.length>0) { + List<Annotation> la = new ArrayList<Annotation>(); + for (Annotation anno: annos) { + la.add(anno); + } + Collections.<Annotation>sort(la,new AnnoComparator()); + + System.out.println("Annotation count is "+annos.length); + for (Annotation anno: la) { + System.out.println(anno); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + static class AnnoComparator implements Comparator<Annotation> { + public int compare(Annotation a, Annotation b) { + return a.toString().compareTo(b.toString()); + } + } +} diff --git a/tests/bugs170/xmldefs/Hello5.java b/tests/bugs170/xmldefs/Hello5.java new file mode 100644 index 000000000..361415a3e --- /dev/null +++ b/tests/bugs170/xmldefs/Hello5.java @@ -0,0 +1,42 @@ +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.*; +public class Hello5 { + + public static int field1; + public static int field2; + + public static void main(String[] args) { + printAnnos("field1"); + printAnnos("field2"); + } + + public static void printAnnos(String fieldname) { + try { + Field m = Hello5.class.getDeclaredField(fieldname); + Annotation[] annos = m.getAnnotations(); + System.out.println("Annotations on "+fieldname+"? "+(annos!=null && annos.length!=0)); + if (annos!=null && annos.length>0) { + List<Annotation> la = new ArrayList<Annotation>(); + for (Annotation anno: annos) { + la.add(anno); + } + Collections.<Annotation>sort(la,new AnnoComparator()); + + System.out.println("Annotation count is "+annos.length); + for (Annotation anno: la) { + System.out.println(anno); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + static class AnnoComparator implements Comparator<Annotation> { + public int compare(Annotation a, Annotation b) { + return a.toString().compareTo(b.toString()); + } + } +} diff --git a/tests/bugs170/xmldefs/Hello6.java b/tests/bugs170/xmldefs/Hello6.java new file mode 100644 index 000000000..568a5b46e --- /dev/null +++ b/tests/bugs170/xmldefs/Hello6.java @@ -0,0 +1,41 @@ +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.*; + +public class Hello6 { + + public static int field1; + + public static void main(String[] args) { + printAnnos("field1"); + } + + public static void printAnnos(String fieldname) { + try { + Field m = Hello6.class.getDeclaredField(fieldname); + Annotation[] annos = m.getAnnotations(); + System.out.println("Annotations on "+fieldname+"? "+(annos!=null && annos.length!=0)); + if (annos!=null && annos.length>0) { + List<Annotation> la = new ArrayList<Annotation>(); + for (Annotation anno: annos) { + la.add(anno); + } + Collections.<Annotation>sort(la,new AnnoComparator()); + + System.out.println("Annotation count is "+annos.length); + for (Annotation anno: la) { + System.out.println(anno); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + static class AnnoComparator implements Comparator<Annotation> { + public int compare(Annotation a, Annotation b) { + return a.toString().compareTo(b.toString()); + } + } +} diff --git a/tests/bugs170/xmldefs/aop.xml b/tests/bugs170/xmldefs/aop.xml new file mode 100644 index 000000000..afa85168b --- /dev/null +++ b/tests/bugs170/xmldefs/aop.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<aspectj> + <aspects> + <concrete-aspect name="ConjuredUp"> + <declare-annotation method="* Hello.sayHello(..)" annotation="@Anno"/> + </concrete-aspect> + </aspects> + + <weaver options="-Xreweavable -verbose -XlazyTjp -showWeaveInfo"> + <include within="Hello"/> + </weaver> +</aspectj> + diff --git a/tests/bugs170/xmldefs/aop2.xml b/tests/bugs170/xmldefs/aop2.xml new file mode 100644 index 000000000..d5ebdf7af --- /dev/null +++ b/tests/bugs170/xmldefs/aop2.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<aspectj> + <aspects> + <concrete-aspect name="ConjuredUp"> + <declare-annotation method="* Hello.sayHello(..)" annotation="@Anno2"/> + </concrete-aspect> + </aspects> + + <weaver options="-Xreweavable -verbose -XlazyTjp -showWeaveInfo"> + <include within="Hello"/> + </weaver> +</aspectj> + diff --git a/tests/bugs170/xmldefs/aop3.xml b/tests/bugs170/xmldefs/aop3.xml new file mode 100644 index 000000000..a65a552ed --- /dev/null +++ b/tests/bugs170/xmldefs/aop3.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<aspectj> + <aspects> + <concrete-aspect name="ConjuredUp"> + <declare-annotation field="* i" annotation="@Anno"/> + </concrete-aspect> + </aspects> + + <weaver options="-Xreweavable -verbose -XlazyTjp -showWeaveInfo"> + <include within="Hello2"/> + </weaver> +</aspectj> + diff --git a/tests/bugs170/xmldefs/aop4.xml b/tests/bugs170/xmldefs/aop4.xml new file mode 100644 index 000000000..1cf61807b --- /dev/null +++ b/tests/bugs170/xmldefs/aop4.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<aspectj> + <aspects> + <concrete-aspect name="ConjuredUp"> + <declare-annotation field="* field1(..)" annotation="@AnnoString("set from xml")"/> + <declare-annotation field="* field1(..)" annotation="@AnnoLong(999)"/> + <declare-annotation field="* field1(..)" annotation="@AnnoBoolean(true)"/> + <declare-annotation field="* field1(..)" annotation="@AnnoClass(Integer.class)"/> + <declare-annotation field="* field2(..)" annotation="@AnnoClass()"/> <!-- testing empty paren --> + </concrete-aspect> + </aspects> + + <weaver options="-Xreweavable -verbose -XlazyTjp -showWeaveInfo"> + <include within="Hello4"/> + </weaver> +</aspectj> + diff --git a/tests/bugs170/xmldefs/aop5.xml b/tests/bugs170/xmldefs/aop5.xml new file mode 100644 index 000000000..31a5f7d48 --- /dev/null +++ b/tests/bugs170/xmldefs/aop5.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<aspectj> + <aspects> + <concrete-aspect name="ConjuredUp"> + <declare-annotation field="* field1(..)" annotation="@AnnoShort(8)"/> + <declare-annotation field="* field1(..)" annotation="@AnnoChar('z')"/> + <declare-annotation field="* field1(..)" annotation="@AnnoDouble(99.0d)"/> + <declare-annotation field="* field1(..)" annotation="@AnnoFloat(6.0f)"/> + <declare-annotation field="* field2(..)" annotation="@AnnoByte(88)"/> + <declare-annotation field="* field2(..)" annotation="@AnnoInt(99)"/> + </concrete-aspect> + </aspects> + + <weaver options="-Xreweavable -verbose -XlazyTjp -showWeaveInfo"> + <include within="Hello5"/> + </weaver> +</aspectj> + diff --git a/tests/bugs170/xmldefs/aop6.xml b/tests/bugs170/xmldefs/aop6.xml new file mode 100644 index 000000000..06300b72f --- /dev/null +++ b/tests/bugs170/xmldefs/aop6.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<aspectj> + <aspects> + <concrete-aspect name="ConjuredUp"> + <declare-annotation field="* field1(..)" annotation="@Annot(a='a',fred=false,'abc')"/> + </concrete-aspect> + </aspects> + + <weaver options="-Xreweavable -verbose -XlazyTjp -showWeaveInfo"> + <include within="Hello6"/> + </weaver> +</aspectj> + diff --git a/tests/bugs170/xmldefs/aop6a.xml b/tests/bugs170/xmldefs/aop6a.xml new file mode 100644 index 000000000..2c6410a9b --- /dev/null +++ b/tests/bugs170/xmldefs/aop6a.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<aspectj> + <aspects> + <concrete-aspect name="ConjuredUp"> + <declare-annotation field="* field1(..)" annotation="@Annot( a= 'a' , fred = false, 'abc' )"/> + </concrete-aspect> + </aspects> + + <weaver options="-Xreweavable -verbose -XlazyTjp -showWeaveInfo"> + <include within="Hello6"/> + </weaver> +</aspectj> + diff --git a/tests/src/org/aspectj/systemtest/ajc170/Ajc170Tests.java b/tests/src/org/aspectj/systemtest/ajc170/Ajc170Tests.java index 9ce29a36f..907464990 100644 --- a/tests/src/org/aspectj/systemtest/ajc170/Ajc170Tests.java +++ b/tests/src/org/aspectj/systemtest/ajc170/Ajc170Tests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008 Contributors + * Copyright (c) 2008-2012 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 @@ -17,20 +17,13 @@ import junit.framework.Test; import org.aspectj.apache.bcel.classfile.Field; import org.aspectj.apache.bcel.classfile.JavaClass; import org.aspectj.testing.XMLBasedAjcTestCase; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.TypeFactory; import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.World; import org.aspectj.weaver.internal.tools.StandardPointcutExpressionImpl; -import org.aspectj.weaver.patterns.BasicTokenSource; -import org.aspectj.weaver.patterns.ITokenSource; -import org.aspectj.weaver.patterns.PatternParser; import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.PointcutEvaluationExpenseComparator; import org.aspectj.weaver.patterns.PointcutRewriter; import org.aspectj.weaver.reflect.ReflectionWorld; -import org.aspectj.weaver.tools.StandardPointcutExpression; import org.aspectj.weaver.tools.StandardPointcutParser; /** @@ -38,6 +31,35 @@ import org.aspectj.weaver.tools.StandardPointcutParser; */ public class Ajc170Tests extends org.aspectj.testing.XMLBasedAjcTestCase { + public void testXmlDefsDeclareAnnoMethod() { + runTest("xml defined dec at method"); + } + + // anno not runtime vis + public void testXmlDefsDeclareAnnoMethod2() { + runTest("xml defined dec at method 2"); + } + + public void testXmlDefsDeclareAnnoField() { + runTest("xml defined dec at field"); + } + + public void testXmlDefsDeclareAnnoFieldVariants1() { + runTest("xml defined dec anno - variants 1"); + } + + public void testXmlDefsDeclareAnnoFieldVariants2() { + runTest("xml defined dec anno - variants 2"); + } + + public void testXmlDefsDeclareAnnoFieldMultipleValues() { + runTest("xml defined dec anno - multiple values"); + } + + public void testXmlDefsDeclareAnnoFieldMultipleValuesAndSpaces() { + runTest("xml defined dec anno - multiple values and spaces"); + } + public void testPointcutExpense_374964() { // check a declaring type being specified causes the call() to be considered cheaper than this() diff --git a/tests/src/org/aspectj/systemtest/ajc170/ajc170.xml b/tests/src/org/aspectj/systemtest/ajc170/ajc170.xml index 58c936905..daed3a8e7 100644 --- a/tests/src/org/aspectj/systemtest/ajc170/ajc170.xml +++ b/tests/src/org/aspectj/systemtest/ajc170/ajc170.xml @@ -2,6 +2,106 @@ <suite> + + <ajc-test dir="bugs170/xmldefs" title="xml defined dec at method"> + <compile files="Hello.java Anno.java" options="-1.5"/> + <run class="Hello" ltw="aop.xml"> + <stdout> + <line text="Hello"/> + <line text="World"/> + <line text="Annotations on sayHello? true"/> + <line text="Annotation count is 1"/> + <line text="@Anno"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs170/xmldefs" title="xml defined dec at method 2"> + <compile files="Hello.java Anno2.java" options="-1.5"/> + <run class="Hello" ltw="aop2.xml"> + <stdout> + <line text="Hello"/> + <line text="World"/> + <line text="Annotations on sayHello? false"/> + </stdout> + <stderr> + <line text="info AspectJ Weaver"/> + <line text="info register"/> + <line text="info using configuration"/> + <line text="info define aspect ConjuredUp"/> + <line text="error declare is using an annotation type that does not have runtime retention: @Anno2"/> + <line text="info weaver"/> + </stderr> + </run> + </ajc-test> + + <ajc-test dir="bugs170/xmldefs" title="xml defined dec at field"> + <compile files="Hello2.java Anno.java" options="-1.5"/> + <run class="Hello2" ltw="aop3.xml"> + <stdout> + <line text="Annotations on i? true"/> + <line text="Annotation count is 1"/> + <line text="@Anno"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs170/xmldefs" title="xml defined dec anno - variants 1"> + <compile files="Hello4.java Anno4.java" options="-1.5"/> + <run class="Hello4" ltw="aop4.xml"> + <stdout> + <line text="Annotations on field1? true"/> + <line text="Annotation count is 4"/> + <line text="@AnnoBoolean(zzz=false, value=true)"/> + <line text="@AnnoClass(value=class java.lang.Integer, ccc=class java.lang.String)"/> + <line text="@AnnoLong(jjj=111, value=999)"/> + <line text="@AnnoString(sss=xyz, value=set from xml)"/> + <line text="Annotations on field2? true"/> + <line text="Annotation count is 1"/> + <line text="@AnnoClass(value=class java.lang.String, ccc=class java.lang.String)"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs170/xmldefs" title="xml defined dec anno - variants 2"> + <compile files="Hello5.java Anno5.java" options="-1.5"/> + <run class="Hello5" ltw="aop5.xml"> + <stdout> + <line text="Annotations on field1? true"/> + <line text="Annotation count is 4"/> + <line text="@AnnoChar(value=z, ccc=a)"/> + <line text="@AnnoDouble(ddd=3.0, value=99.0)"/> + <line text="@AnnoFloat(fff=4.0, value=6.0)"/> + <line text="@AnnoShort(sss=3, value=8)"/> + <line text="Annotations on field2? true"/> + <line text="Annotation count is 2"/> + <line text="@AnnoByte(value=88, bbb=66)"/> + <line text="@AnnoInt(value=99, iii=111)"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs170/xmldefs" title="xml defined dec anno - multiple values"> + <compile files="Hello6.java Anno6.java" options="-1.5"/> + <run class="Hello6" ltw="aop6.xml"> + <stdout> + <line text="Annotations on field1? true"/> + <line text="Annotation count is 1"/> + <line text="@Annot(a=a, fred=false, value=abc)"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs170/xmldefs" title="xml defined dec anno - multiple values and spaces"> + <compile files="Hello6.java Anno6.java" options="-1.5"/> + <run class="Hello6" ltw="aop6a.xml"> + <stdout> + <line text="Annotations on field1? true"/> + <line text="Annotation count is 1"/> + <line text="@Annot(a=a, fred=false, value=abc)"/> + </stdout> + </run> + </ajc-test> <ajc-test dir="bugs170/pr73507" title="public ITDfs - 1"> <compile files="Case1.java" options="-1.5"/> diff --git a/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java b/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java index d8a9c0e0e..62ebac92c 100644 --- a/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java +++ b/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java @@ -1578,6 +1578,10 @@ public final class LazyClassGen { annotations.add(new AnnotationGen(a, getConstantPool(), true)); } } + + public void addAttribute(AjAttribute attribute) { + myGen.addAttribute(Utility.bcelAttribute(attribute, getConstantPool())); + } // this test is like asking: // if diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java b/weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java index 7f9ce729d..de780156e 100644 --- a/weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java +++ b/weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java @@ -112,6 +112,7 @@ public class Definition { public final String extend; public final String precedence; public final List<Definition.Pointcut> pointcuts; + public final List<Definition.DeclareAnnotation> declareAnnotations; public final List<Definition.PointcutAndAdvice> pointcutsAndAdvice; public final String perclause; public List<Definition.DeclareErrorOrWarning> deows; @@ -135,6 +136,7 @@ public class Definition { } this.precedence = precedence; this.pointcuts = new ArrayList<Definition.Pointcut>(); + this.declareAnnotations = new ArrayList<Definition.DeclareAnnotation>(); this.pointcutsAndAdvice = new ArrayList<Definition.PointcutAndAdvice>(); this.deows = new ArrayList<Definition.DeclareErrorOrWarning>(); this.perclause = perclause; @@ -154,6 +156,22 @@ public class Definition { public enum AdviceKind { Before, After, AfterReturning, AfterThrowing, Around; } + + public enum DeclareAnnotationKind { + Method, Field, Type; + } + + public static class DeclareAnnotation { + public final DeclareAnnotationKind declareAnnotationKind; + public final String pattern; + public final String annotation; + + public DeclareAnnotation(DeclareAnnotationKind kind, String pattern, String annotation) { + this.declareAnnotationKind = kind; + this.pattern = pattern; + this.annotation = annotation; + } + } public static class PointcutAndAdvice { public final AdviceKind adviceKind; diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java b/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java index 13968a696..7ff275fd7 100644 --- a/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java +++ b/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java @@ -22,6 +22,8 @@ import javax.xml.parsers.SAXParserFactory; import org.aspectj.util.LangUtil; import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind; +import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotation; +import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -72,6 +74,7 @@ public class DocumentParser extends DefaultHandler { private final static String AROUND_ELEMENT = "around"; private final static String WITHIN_ATTRIBUTE = "within"; private final static String EXPRESSION_ATTRIBUTE = "expression"; + private final static String DECLARE_ANNOTATION_ELEMENT = "declare-annotation"; private final Definition definition; @@ -226,6 +229,30 @@ public class DocumentParser extends DefaultHandler { if (!isNull(name) && !isNull(expression)) { activeConcreteAspectDefinition.pointcuts.add(new Definition.Pointcut(name, replaceXmlAnd(expression))); } + } else if (DECLARE_ANNOTATION_ELEMENT.equals(qName) && activeConcreteAspectDefinition!=null) { + String methodSig = attributes.getValue("method"); + String fieldSig = attributes.getValue("field"); + String typePat = attributes.getValue("type"); + String anno = attributes.getValue("annotation"); + if (isNull(anno)) { + throw new SAXException("Badly formed <declare-annotation> element, 'annotation' value is missing"); + } + if (isNull(methodSig) && isNull(fieldSig) && isNull(typePat)) { + throw new SAXException("Badly formed <declare-annotation> element, need one of 'method'/'field'/'type' specified"); + } + if (!isNull(methodSig)) { + // declare @method + activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Method, + methodSig, anno)); + } else if (!isNull(fieldSig)) { + // declare @field + activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Field, + fieldSig, anno)); + } else if (!isNull(typePat)) { + // declare @type + activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Type, + typePat, anno)); + } } else if (BEFORE_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { String pointcut = attributes.getValue(POINTCUT_ELEMENT); String adviceClass = attributes.getValue("invokeClass"); |