From reinier at zwitserloot.com Mon Feb 6 14:58:41 2012 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Mon, 6 Feb 2012 23:58:41 +0100 Subject: Opportunistic coin improvements for JDK8: Can those be tracked someplace? (Any annotation in annotations) In-Reply-To: <4EAB474D.7000007@oracle.com> References: <4EAB474D.7000007@oracle.com> Message-ID: Good news! - Phew! Annotations *ARE* stored as: [Member Name, Member Type, Member Value] tuples, therefore adding this feature is very simple. No changes to the class file format are required (in fact, not a single character in the entire JVMS needs an update, not even examples or clarifications), and for reflection + javac support, only 3 lines need to be patched. javax.lang.model is a little more work, and a loop detector needs to be added for default values in Check.java, but all in all it's about a page and a half of patch. We'll post a complete proposal including a full patch for JLS, javac, and JVM, and an experimentation tool (a java agent which, if added to javac, will 'live patch' any javac7 for quick experimentation, no need to build a new javac from scratch or look around for a prebuilt binary for your platform!) in a few hours. From reinier at zwitserloot.com Mon Feb 6 15:39:59 2012 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Tue, 7 Feb 2012 00:39:59 +0100 Subject: What is the appropriate venue for a very small JDK8 proposal that includes complete patches to javac, JDK, and JLS? Message-ID: I'll post a complete proposal for the 'Any Annotation' feature in just a second, but as coin isn't really happening for JDK8 I'm guessing this mailing list is not the right venue. Where should I post this? --Reinier Zwitserloot From reinier at zwitserloot.com Mon Feb 6 15:40:32 2012 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Tue, 7 Feb 2012 00:40:32 +0100 Subject: AnyAnnotation Proposal Message-ID: # The AnyAnnotation feature ___v1.0___ Currently, the JLS specifies that only a direct subtype of `java.lang.annotation.Annotation`, defined using the `public @interface` syntax, is actually an annotation type, and that the return type of any member of an annotation declaration can only be an annotation type (or a primitive, String, Class, or a 1-dimensional array). It is therefore not possible in java 1.7 to create a member of an annotation declaration that implies 'any annotation is legal here'. This proposal addresses the lack of this feature. It also moves java closer towards supporting hierarchical annotations (the ability to have one annotation declaration extend something other than `java.lang.annotation.Annotation` itself). In a nutshell, the proposed changes: * Allow `java.lang.annotation.Annotation` as a valid return type for annotation declaration members (in JDK 1.7 this is not legal). Using this class as return type means any annotation is intended to be legal. * Update the JLS to reflect this change (no changes to javadoc, reflection core API, and JVM specification needed). * Patch an assert statement in the reflection core API, and 2 lines in javac, to support the spec. * Add a new loop detection scheme to avoid an endless loop in the `default` construction of an annotation declaration member. * Expand on the impact this change will have, both in regards to which tooling options now become possible and what impact this change can have on existing tools. ## Authors * Reinier Zwitserloot (r.zwitserloot at projectlombok.org) * Roel Spilker (r.spilker at projectlombok.org) * Sander Koning (s.koning at projectlombok.org) * Robbert Jan Grootjans (r.grootjans at projectlombok.org) ## Try it right now! A special try-it-out javac agent is available here: [ http://projectlombok.org/anyannotation](http://projectlombok.org/anyannotation) This agent 'live patches' any javac7 in memory only, so you can start experimenting right away with this feature. ## Changes required in the JDK and associated documentation ### JVM Specification: Zero changes required _Based on [Java Virtual Machine Specification SE7][JVMS]_ 1. For annotations themselves: Section 4.7.16 - **The RuntimeVisibleAnnotations attribute** through Section 4.7.19 - **The RuntimeInvisibleParameterAnnotations attribute** specify how annotations should be encoded in the class file. These sections say the only way to store an annotation inside an annotation is as a new annotation block, and such a block _includes_ the type of the annotation. Thus, in `@Foo(@Bar)`, there is always a pointer to the constant pool entry with `com.package.Bar`. It is therefore not necessary to rely on the signatures available in the `Foo` type to understand its parameter, and therefore no change is necessary to the class file format to support the case where `@Foo`'s parameter is of type `Annotation`. 2. For annotation type declarations, the JVMS basically contains no specifications. They are just plain interfaces with a bit set (`ACC_ANNOTATION` - see section 4.1 `access_flags`). The only new aspect (other than the `ACC_ANNOTATION` bit, which is not affected by this proposal) is the classfile encoding of the `default` value feature of an annotation declaration member, which is described in section 4.7.20 - **The AnnotationDefault attribute**. The format of this attribute is explained in terms of section 4.7.16's annotation structure, which already supports this proposal without any changes required. There is absolutely no mention that the value of an _AnnotationDefault_ block has to match the type of the `method_info`'s return type (that aspect of the annotation spec is covered in the JLS only). In other words, the JVMS doesn't actually consider `int foo() default "Hello, World!";` illegal, though javac obviously would refuse to emit a class file if you tried it. This means that `Annotation onMethod() default @Deprecated;` isn't treated as illegal by the JVMS either, and thus the JVMS needs no updates to reflect that this would be a legal construct now. There is furthermore no commentary about the fact that the return type of an element of an `ACC_ANNOTATION` type can only be a primitive, String, Class, an annotation type, or a 1-dimensional array of those. Therefore, no comment needs to be added to explain that `java.lang.annotation.Annotation` is also legal even though it is not an annotation type according to the JLS. ### JLS Specification: A few changes required _based on [Java Language Specification SE7][JLS]_ 1. 9.6 - **Annotation Types** This introductory section describes the actual declaration of an annotation type; no changes needed. 2. 9.6.1 - **Annotation Type Elements** requires two changes: _old:_ It is a compile-time error if the return type of a method declared in an annotation type is not one of the following: a primitive type, String, Class, any parameterized invocation of Class, an enum type (S8.9), an annotation type, or an array type (S10) whose element type is one of the preceding types. _new:_ It is a compile-time error if the return type of a method declared in an annotation type is not one of the following: a primitive type, String, Class, any parameterized invocation of Class, an enum type (S8.9), an annotation type, `java.lang.annotation.Annotation`, or an array type (S10) whose element type is one of the preceding types. [smaller] If the return type of an annotation method is declared to be `java.lang.annotation.Annotation` (or its array), any annotation (S9.7) is a valid value: @interface Getter { Annotation[] onMethod() default {@NonNull}; } [/smaller] _old:_ It is a compile-time error if an annotation type declaration T contains an element of type T, either directly or indirectly. [smaller] For example, this is illegal: @interface SelfRef { SelfRef value(); } and so is this: @interface Ping { Pong value(); } @interface Pong { Ping value(); } [/smaller] _new (optional change; add an example to clarify):_ [smaller] For example, this is illegal: @interface SelfRef { SelfRef value(); } and so is this: @interface Ping { Pong value(); } @interface Pong { Ping value(); } as is this: @interface SelfRef { Annotation value() default @SelfRef; } [/smaller] 3. 9.6.2 - **Defaults for Annotation Type Elements** requires no changes. The relevant paragraph reads: It is a compile-time error if the type of the element is not commensurate (S9.7) with the default value specified. After applying the required changes to sections S9.6.1 and S9.7.1, this statement is still valid. 4. 9.6.3 - **Predefined Annotation Types** requires no changes. 5. 9.7 - **Annotations** requires no changes (this is an introduction section). 6. 9.7.1 - **Normal Annotations** requires one change and one optional change. The production rule for _ElementValue_ does not need changing because it already mentions it can consist of an _Annotation_. _old_: The return type of this method defines the element type of the element-value pair. _new_: The return type of this method defines the element type of the element-value pair. If the return type is `java.lang.annotation.Annotation`, any annotation is a valid value. _old_: The type of V is assignment compatible (S5.2) with T, and furthermore: * If T is a primitive type or String, and V is a constant expression (S15.28). * V is not null. * If T is Class, or an invocation of Class, and V is a class literal (S15.8.2). * If T is an enum type, and V is an enum constant. _append an item to the list as clarification:_ * If T is `java.lang.annotation.Annotation`, and V is an annotation. 7. 9.7.2 - **Marker Annotations** only describes a shorthand notation; needs no changes. 8. 9.7.3 - **Single-Element Annotations** only describes a shorthand notation; needs no changes. 9. 13.5.7 - **Evolution of Annotation Types** needs no changes. ### Changes to javadoc of existing java.* API: No changes required 1. method `java.lang.reflect.Method.getDefaultValue()`'s javadoc does not mention anything about annotation member's return types being restricted to a subset of legal types, therefore no update to include `java.lang.annotation.Annotation` in this subset is required. 2. `java.lang.Class.isAnnotation()` (and `getModifiers() & java.lang.reflect.Modifier.ANNOTATION`) is the only aspect of `Class` which is different / relevant for annotation types, and it also makes no mention of the return type limitations. 3. Existing annotations in the java base library itself (`@Override`, `@Deprecated`, etc - listed in JLS sections 9.6.3.1-9.6.3.7) do not have any methods which are an annotation type, nor do any of these types seem like they could use one. No updates are required or suggested for any of them. 4. The javadoc on `java.lang.annotation.Annotation` itself remains valid. It might be prudent to expand it with a section that explains that it can be used as a return type for an annotation method, but the other legal return types for annotation declaration members don't have this either. Therefore, for consistency's sake, this proposal does not include a change to this javadoc. ### Changes / additions to any of the method signatures of the reflection API or any other part of java base: No changes required * `java.lang.reflect.Method.getDefaultValue()` already returns `java.lang.Object` and thus needs no changes. * No new API is required to reflectively determine that a given annotation declaration member's return type is `Annotation`, because the way this return type is reflected is via a `java.lang.Class` return type, which is already capable of conveying `Annotation` as a value. This part is one of the few ways existing tools might break, as they may erroneously assume this return value can only be `java.lang.Class.class`, `java.lang.String.class`, any of the primitive wrappers, or a type which is an annotation type. This method could now also return `java.lang.annotation.Annotation` which is not itself an annotation type. * No new API is required to reflectively read out annotation values, as these will still be specific instances of annotations. * No new API is required to reflectively read out annotation defaults, as these, if present, will still be specific instances of annotations. ### Changes / additions to JVM library reflection core (java.*): No changes needed 1. `java.lang.reflect.Method.getDefaultValue()` delegates work to internal implementations and does not contain any code that would cause issues with a `java.lang.annotation.Annotation` return type. It does first acquire the return type of the method and then asks for an instance of the value that 'fits' this return type, but it leaves all checking to the internal implementations that provide both of these values (both the return type and an instance for the value given the byte array containing the raw `annotationDefault` data). The work is deferred to `sun.reflect.annotation.AnnotationParser` and `sun.reflection.annotation.AnnotationType`, which do need patches (see below). 2. `java.lang.reflect.Method.getAnnotation(java.lang.Class annotationClass)` defers work to `sun.reflect.annotation.AnnotationParser` and `sun.reflect.annotation.AnnotationType` as well. Same for `java.lang.reflect.Field.getAnnotation`, and `java.lang.Class.getAnnotation`. `java.lang.Package.getAnnotation` is a wrapper around a dummy `java.lang.Class` instance that holds annotation data (and thus, it too defers the work). These internal helpers do need patches (see below). ### Changes / additions to internal support classes used by reflective core: One tiny change needed to a system assertion 1. `sun.reflect.annotation.AnnotationType` - no changes necessary. 2. `sun.reflect.annotation.AnnotationInvocationHandler` - no changes necessary. In particular, the `equals()` implementation does not use the return type, only member values; all special handling defaults to `equals()`, `toString()`, `hashCode()` etc of the member value if the member value is an annotation type. annotation instances already have working `equals`, `hashCode`, `toString`, etc implementations, therefore no changes are necessary. 3. `sun.reflect.annotation.AnnotationParser` - Minor changes necessary. _based on [Revision 9b8c96f96a0f of AnnotationParser.java][AnnotationParser]_ * method `parseMemberValue` does NOT provide the expected type (parameter `memberType`) to the `parseAnnotation` helper method. Therefore, the fact that expected type is a previously invalid value (`java.lang.annotation.Annotation`) does not have any effect on parsing the annotation in the class file data. * method `parseSig` does not check if the provided type is an annotation type. (It really can't, as the type is not guaranteed to be on the classpath, and therefore it has no way of knowing if the provided type is actually an annotation type. Nevertheless, the code does indeed include no such check). * method 'parseArray' (helper of `parseMemberValue`) contains an assertion (which can be enabled with the `-esa` javac option) on line 485 which needs updating: - assert componentType.isAnnotation(); + assert componentType.isAnnotation() || componentType == java.lang.annotation.Annotation.class; ### Changes to javac: Some changes needed * javac checks that an annotation declaration member's return type is one of the allowed types. This check needs to be extended to consider `java.lang.annotation.Annotation` a legal return type value as well. No change in the way javac builds the class file to represent the annotation declaration is required: In com.sun.tools.javac.comp.Check:2267 _based on [Revision ce654f4ecfd8 of Check.java][Check]_ - if ((type.tsym.flags() & Flags.ANNOTATION) != 0) { + if ((type.tsym.flags() & Flags.ANNOTATION) != 0 || || types.isSameType(type, syms.annotationType)) { * Loop detection: It is now possible to create 'loops' in default values, where the default value of an annotation is itself, or, indirectly, some other annotation, one of whose methods contains a default value that points back itself. Prior to the introduction of this feature, the rule that the return types of annotation methods cannot contain a cyclic reference would make it impossible for the default value to contain such a loop, but now this is no longer true, so a separate loop detection scheme needs to be implemented for default values. NB: The `checkAnnotationResType` method has been renamed in this patch to `checkAnnotationElementType` because it is now no longer used just to check return types, but also to check the types in a `default` value. This change is a bit more involved and thus the full patch is listed here in posix diff format: Full patch of com.sun.tools.javac.comp.Check: _based on [Revision ce654f4ecfd8 of Check.java][Check]_ 29d28 < import java.util.Set; 33a33,34 > import com.sun.tools.javac.tree.JCTree.JCAnnotation; > import com.sun.tools.javac.tree.JCTree.JCExpression; 38a40 > import com.sun.tools.javac.code.Attribute.Array; 40a43 > import com.sun.tools.javac.code.Type; 2267c2270 < if ((type.tsym.flags() & Flags.ANNOTATION) != 0) return; --- > if ((type.tsym.flags() & Flags.ANNOTATION) != 0 || types.isSameType(type, syms.annotationType)) return; 2497c2500,2501 < checkAnnotationResType(meth.pos(), meth.restype.type); --- > checkAnnotationElementType(meth.pos(), meth.restype.type); > checkNonCyclicAnnotationDefaultValues(meth); 2518c2522,2523 < checkAnnotationResType(pos, ((MethodSymbol)s).type.getReturnType()); --- > checkAnnotationElementType(pos, ((MethodSymbol)s).type.getReturnType()); > checkNonCyclicAnnotationDefaultValues(pos, (MethodSymbol)s); 2526c2531 < void checkAnnotationResType(DiagnosticPosition pos, Type type) { --- > void checkAnnotationElementType(DiagnosticPosition pos, Type type) { 2533c2538 < checkAnnotationResType(pos, types.elemtype(type)); --- > checkAnnotationElementType(pos, types.elemtype(type)); 2539a2545,2579 > private void checkNonCyclicAnnotationDefaultValues(JCMethodDecl meth) { > if (!isAnnotationType(meth.restype.type)) return; > if (meth.defaultValue == null) return; > meth.defaultValue.accept(new TreeScanner() { > @Override public void visitAnnotation(JCAnnotation tree) { > checkAnnotationElementType(tree.pos(), tree.type); > super.visitAnnotation(tree); > } > }); > } > > private void checkNonCyclicAnnotationDefaultValues(final DiagnosticPosition pos, MethodSymbol meth) { > if (!isAnnotationType(meth.type.getReturnType())) return; > if (meth.defaultValue == null) return; > if (meth.defaultValue.type.tag == TypeTags.ARRAY) { > for (Attribute a : ((Array)meth.defaultValue).values) { > checkAnnotationElementType(pos, a.type); > } > } > else { > checkAnnotationElementType(pos, meth.defaultValue.type); > } > } > > private boolean isAnnotationType(Type type) { > switch (type.tag) { > case TypeTags.CLASS: > return types.isSameType(type, syms.annotationType); > case TypeTags.ARRAY: > return types.isSameType(types.elemtype(type), syms.annotationType); > default: > return false; > } > } > * The error message with key 'cyclic.annotation.element' doesn't need changing. * javac checks that an annotation's parameter is type compatible with the annotation's declaration. It does this using an 'assignment compatible' check, which will work fine with `java.lang.annotation.Annotation` as return type of the annotation declaration member method. However, this check is entered using an `if` statement which needs to be expanded: In com.sun.tools.javac.comp.Annotate:224 _based on [Revision ce654f4ecfd8 of Annotate.java][Annotate]_ - if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) { + if ((expected.tsym.flags() & Flags.ANNOTATION) != 0 || types.isSameType(expected, syms.annotationType)) { * No other changes are required. The error message with key `invalid.annotation.member.type` may need to be expanded to explain that `Annotation` is also a legal type. ### Changes to javax.lang.model: Some changes needed * javax.lang.model mostly requires no changes, except for the feature where one can ask javax.lang.model to create an instance of a given annotation class, i.e.: for (Element elem : roundEnv.getRootElements()) { SomeAnnotation instanceOfAnnotation = elem.getAnnotation(SomeAnnotation.class); } The implementation of this feature in OpenJDK's javac obtains `java.lang.Class` instances (needed to create the proxies) entirely from traversing `SomeAnnotation.class` using reflection. As the return types of the methods in `SomeAnnotation.class` would be `java.lang.annotation.Annotation`, this mechanism is no longer useful. Instead, the 'flat name' of the annotation argument itself needs to be turned into a `java.lang.Class` by using `Class.forName()`. Also, it is now possible for an annotation argument to contain an annotation that is not on the classpath of the annotation processor. The right approach is for this annotation instance to throw a `TypeMirrorException` as late as is feasible (when the annotation method is invoked that would have to return an instance of a type that is not available, but not when i.e. `toString()` is invoked). A full patch to the appropriate class is listed here (note that the alternate strategy of using `Class.forName` is only used for the new feature; existing annotations that do not use it are still created by traversing the annototation type via reflection): In com.sun.tools.javac.model.AnnotationProxyMaker _based on [Revision ce654f4ecfd8 of AnnotationProxyMaker.java][AnnotationProxyMaker]_ 62c62,64 < private final Class annoType; --- > private Class annoType; > private final Class context; > private ClassLoader classLoader; 66c68 < Class annoType) { --- > Class annoType, Class context) { 68a71 > this.context = context; 77,78c80 < AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType); < return annoType.cast(apm.generateAnnotation()); --- > return annoType.cast(generateAnnotationInner(anno, annoType, annoType, null)); 80a83,88 > private static Object generateAnnotationInner( > Attribute.Compound anno, Class annoType, Class context, ClassLoader classLoader) { > AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType, context); > apm.classLoader = classLoader; > return apm.generateAnnotation(); > } 81a90,100 > private ClassLoader getAnnotationClassLoader() { > if (classLoader == null) { > ClassLoader cl = context.getClassLoader(); > // Line above Could cause security exception, but > // no other part of javac uses doPrivileged; > // in particular line 259 of AnnotationParser also doesn't bother, > // and is in the same boat. > this.classLoader = cl != null ? cl : ClassLoader.getSystemClassLoader(); > } > return classLoader; > } 85c104,116 < private Annotation generateAnnotation() { --- > @SuppressWarnings("unchecked") > private Object generateAnnotation() { > if (annoType == Annotation.class) { > try { > Class clazz = (Class) > getAnnotationClassLoader().loadClass(anno.type.tsym.flatName().toString()); > annoType = clazz; > return AnnotationParser.annotationForMap(clazz, > getAllReflectedValues()); > } catch (ClassNotFoundException e) { > return new MirroredTypeExceptionProxy(anno.type); > } > } 170a202 > 239c271 < value = generateAnnotation(c, nested); --- > value = generateAnnotationInner(c, nested, context, classLoader); ## Source and binary compatibility This proposal introduces no new or changed behaviour for any source code which is legal today, and no changes to the class file format. Therefore, existing source files which compile on javac 1.7 are not affected. Existing tools also notice no changes for existing source and class files. However, newly compiled annotation declarations may now start using `java.lang.annotation.Annotation` as return type for members, and some existing tools may have assumed the legal set of return values couldn't change. These tools will need to be updated. Technically, it is possible for existing annotations to be 'widened' - any return types which used to be a specific annotation type can be generalized to `java.lang.annotation.Annotation`, but this might cause problems with consumers of this annotation. The same problem occurs for any library update where signatures are changed, however. ## Use cases for this feature One obvious use case for annotations is generating code. If this feature is added to the JDK, it is possible to specify a list of annotations that should be put in the generated code. For example, an annotation that will generate a POJO with implementations for `equals`, `hashCode`, et cetera: @GeneratePOJO @AddAnnotations(onClass=@SuppressWarnings("all")) public class StudentTemplate { @AddAnnotations(onGetter={@NonNull, @javax.persistence.Id}) private int unid; } This feature also takes a step towards allowing hierarchical annotation definitions (where an annotation extends another annotation type, instead of `java.lang.annotation.Annotation`. ## Testing the feature These patches, plus a test suite as well as a way to build a 'live patching agent' which allows experimenting with these patches, are available here: * [github repository](http://github.com/rspilker/jdk-proposal.anyannotation) - source repository * [live agent](http://projectlombok.org/anyannotation) - direct download of the agent which allows immediate experimentation with any javac7. [JVMS]: http://docs.oracle.com/javase/7/specs/jvms/JVMS-JavaSE7.pdf [JLS]: http://docs.oracle.com/javase/7/specs/jls/JLS-JavaSE7.pdf [AnnotationParser]: http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/sun/reflect/annotation/AnnotationParser.java [Check]: http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Check.java [Annotate]: http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Annotate.java [AnnotationProxyMaker]: http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java From shams.mahmood at gmail.com Mon Feb 6 17:15:10 2012 From: shams.mahmood at gmail.com (Shams Mahmood) Date: Mon, 6 Feb 2012 17:15:10 -0800 (PST) Subject: Indexing access syntax for Lists and Maps Message-ID: <1328577310.98877.YahooMailNeo@web113613.mail.gq1.yahoo.com> I was wondering whether there has been any update on the indexing feature for Collections? I recently came across Brian Goetz's update on Project Lambda (http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html). It looks like the support for default methods in interfaces can help implement this feature relatively easily: ? package java.lang; ? /** ?? * All classes implementing this interface will have compiler support ?? */ ? interface Indexable { ??? ValueType getAtIndex(KeyType index); ??? ValueType setAtIndex(KeyType index, ValueType value); ? } ? ? package java.util; ? interface List extends Indexable { ?? ... ??? T getAtIndex(Integer index) default { get(index); } ??? T setAtIndex(Integer index, T value) default { set(index, value); value; } ? } ? interface Map extends Indexable { ??? ... ??? V getAtIndex(K index) default { get(index); } ??? V setAtIndex(K index, V value) default { put(index, value); value; } ? } With these interfaces in place the compiler can now desugar the calls for Lists and Maps or similarly implemented classes as: ? myList[1] = myList2[2] = myList3[3]; as: ? myList.setAtIndex(1, myList2.setAtIndex(2, myList3.getAtIndex(3))); Does this sound reasonable? It addresses issues (2), (3) and (4) mentioned below by Reinier. Also, we can possibly can split the interface Indexable into two separate interfaces for readable and writable views to allow users to allow supporting only specific indexing operations. Shams. > Greg, reread some older threads. We've held an extremely extensive? > back-and-forth about implementing map/list index operators. A summary? > of the findings: > > 1. Whatever we come up with, it has to work with java.util.Map and? > java.util.List, otherwise, why bother? Map and List are both? > interfaces, so they cannot gain any new methods. Therefore, either (A)? > we 'hardcode' support in for those data types, or (B) we come up with? > an interface that mirrors a strict subset of the methods in those types. > > 2. Mirroring List's "T get(int)" is just fine, but "boolean set(int,? > T)" is problematic because of that 'boolean' return value. What should? > this: > >?? Object x = someArrayList[0] = "Hello"; >?? System.out.println(x); > > ... > > 3. Same situation (but worse) with Map: "V get(K)" is fine, but "V? > set(K, V)" is problematic, as it returns the OLD value associated with? > the key (usually null, in other words). So:? > System.out.println(map[key] = "Hello"); will print 'null', not? > 'Hello'. Puzzler. Bad. > > 4. Technically, the result of the "a = b" expression in the JLS is? > defined as not just "b", but "b, converted to fit into a". So: > > long foo; > Object x = foo = 1; > System.out.println(x.getClass()); > > will print "java.lang.Long" and not "java.lang.Integer" as you might? > expect... > > 5. Even though I consider the value of this proposal very low (who? > indexes into lists? You .add(), and you iterate), Joe Darcy continues? > to mention this proposal in coin posts, so apparently it isn't dead yet. > >?? --Reinier Zwitserloot From joe.darcy at oracle.com Mon Feb 6 21:42:22 2012 From: joe.darcy at oracle.com (Joe Darcy) Date: Mon, 06 Feb 2012 21:42:22 -0800 Subject: Indexing access syntax for Lists and Maps In-Reply-To: <1328577310.98877.YahooMailNeo@web113613.mail.gq1.yahoo.com> References: <1328577310.98877.YahooMailNeo@web113613.mail.gq1.yahoo.com> Message-ID: <4F30B9BE.5000902@oracle.com> Hello Shams, The presence of extension methods in the platform will make this sort of additional easier, but at least for JDK 8, the major driving features are Project Lambda and Project Jigsaw, with other language-impacting changes being fit in as possible. There are already a number of other language changes under discussion or underway for JDK 8 (JSR 308, repeating annotations, parameter names at runtime [0]), so I wouldn't expect many additions to this list. -Joe [0] http://openjdk.java.net/jeps/0 On 02/06/2012 05:15 PM, Shams Mahmood wrote: > I was wondering whether there has been any update on the indexing feature for Collections? I recently came across Brian Goetz's update on Project Lambda (http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html). It looks like the support for default methods in interfaces can help implement this feature relatively easily: > > package java.lang; > /** > * All classes implementing this interface will have compiler support > */ > interface Indexable { > ValueType getAtIndex(KeyType index); > ValueType setAtIndex(KeyType index, ValueType value); > } > > package java.util; > interface List extends Indexable { > ... > T getAtIndex(Integer index) default { get(index); } > T setAtIndex(Integer index, T value) default { set(index, value); value; } > } > interface Map extends Indexable { > ... > V getAtIndex(K index) default { get(index); } > V setAtIndex(K index, V value) default { put(index, value); value; } > } > > With these interfaces in place the compiler can now desugar the calls for Lists and Maps or similarly implemented classes as: > myList[1] = myList2[2] = myList3[3]; > as: > myList.setAtIndex(1, myList2.setAtIndex(2, myList3.getAtIndex(3))); > > Does this sound reasonable? It addresses issues (2), (3) and (4) mentioned below by Reinier. > > Also, we can possibly can split the interface Indexable into two separate interfaces for readable and writable views to allow users to allow supporting only specific indexing operations. > > Shams. > >> Greg, reread some older threads. We've held an extremely extensive >> back-and-forth about implementing map/list index operators. A summary >> of the findings: >> >> 1. Whatever we come up with, it has to work with java.util.Map and >> java.util.List, otherwise, why bother? Map and List are both >> interfaces, so they cannot gain any new methods. Therefore, either (A) >> we 'hardcode' support in for those data types, or (B) we come up with >> an interface that mirrors a strict subset of the methods in those types. >> >> 2. Mirroring List's "T get(int)" is just fine, but "boolean set(int, >> T)" is problematic because of that 'boolean' return value. What should >> this: >> >> Object x = someArrayList[0] = "Hello"; >> System.out.println(x); >> >> ... >> >> 3. Same situation (but worse) with Map: "V get(K)" is fine, but "V >> set(K, V)" is problematic, as it returns the OLD value associated with >> the key (usually null, in other words). So: >> System.out.println(map[key] = "Hello"); will print 'null', not >> 'Hello'. Puzzler. Bad. >> >> 4. Technically, the result of the "a = b" expression in the JLS is >> defined as not just "b", but "b, converted to fit into a". So: >> >> long foo; >> Object x = foo = 1; >> System.out.println(x.getClass()); >> >> will print "java.lang.Long" and not "java.lang.Integer" as you might >> expect... >> >> 5. Even though I consider the value of this proposal very low (who >> indexes into lists? You .add(), and you iterate), Joe Darcy continues >> to mention this proposal in coin posts, so apparently it isn't dead yet. >> >> --Reinier Zwitserloot From benjamin.john.evans at gmail.com Mon Feb 6 23:48:38 2012 From: benjamin.john.evans at gmail.com (Ben Evans) Date: Tue, 7 Feb 2012 08:48:38 +0100 Subject: AnyAnnotation Proposal In-Reply-To: References: Message-ID: Hi Reinier, Is this the kind of proposal that should really happen as an OpenJDK JEP? Thanks, Ben On Tue, Feb 7, 2012 at 12:40 AM, Reinier Zwitserloot wrote: > # The AnyAnnotation feature > > ___v1.0___ > > Currently, the JLS specifies that only a direct subtype of > `java.lang.annotation.Annotation`, defined using the `public @interface` > syntax, is actually an annotation type, and that the return type of any > member of an annotation declaration can only be an annotation type (or a > primitive, String, Class, or a 1-dimensional array). It is therefore not > possible in java 1.7 to create a member of an annotation declaration that > implies 'any annotation is legal here'. > > This proposal addresses the lack of this feature. It also moves java closer > towards supporting hierarchical annotations (the ability to have one > annotation declaration extend something other than > `java.lang.annotation.Annotation` itself). In a nutshell, the proposed > changes: > > * Allow `java.lang.annotation.Annotation` as a valid return type for > annotation declaration members (in JDK 1.7 this is not legal). Using this > class as return type means any annotation is intended to be legal. > * Update the JLS to reflect this change (no changes to javadoc, reflection > core API, and JVM specification needed). > * Patch an assert statement in the reflection core API, and 2 lines in > javac, to support the spec. > * Add a new loop detection scheme to avoid an endless loop in the `default` > construction of an annotation declaration member. > * Expand on the impact this change will have, both in regards to which > tooling options now become possible and what impact this change can have on > existing tools. > > ## Authors > > * Reinier Zwitserloot (r.zwitserloot at projectlombok.org) > * Roel Spilker (r.spilker at projectlombok.org) > * Sander Koning (s.koning at projectlombok.org) > * Robbert Jan Grootjans (r.grootjans at projectlombok.org) > > ## Try it right now! > > A special try-it-out javac agent is available here: > > [ > http://projectlombok.org/anyannotation](http://projectlombok.org/anyannotation) > > This agent 'live patches' any javac7 in memory only, so you can start > experimenting right away with this feature. > > ## Changes required in the JDK and associated documentation > > ### JVM Specification: Zero changes required > > _Based on [Java Virtual Machine Specification SE7][JVMS]_ > > 1. For annotations themselves: Section 4.7.16 - **The > RuntimeVisibleAnnotations attribute** through Section 4.7.19 - **The > RuntimeInvisibleParameterAnnotations attribute** specify how annotations > should be encoded in the class file. These sections say the only way to > store an annotation inside an annotation is as a new annotation block, and > such a block _includes_ the type of the annotation. Thus, in `@Foo(@Bar)`, > there is always a pointer to the constant pool entry with > `com.package.Bar`. It is therefore not necessary to rely on the signatures > available in the `Foo` type to understand its parameter, and therefore no > change is necessary to the class file format to support the case where > `@Foo`'s parameter is of type `Annotation`. > > 2. For annotation type declarations, the JVMS basically contains no > specifications. They are just plain interfaces with a bit set > (`ACC_ANNOTATION` - see section 4.1 `access_flags`). The only new aspect > (other than the `ACC_ANNOTATION` bit, which is not affected by this > proposal) is the classfile encoding of the `default` value feature of an > annotation declaration member, which is described in section 4.7.20 - **The > AnnotationDefault attribute**. The format of this attribute is explained in > terms of section 4.7.16's annotation structure, which already supports this > proposal without any changes required. There is absolutely no mention that > the value of an _AnnotationDefault_ block has to match the type of the > `method_info`'s return type (that aspect of the annotation spec is covered > in the JLS only). In other words, the JVMS doesn't actually consider `int > foo() default "Hello, World!";` illegal, though javac obviously would > refuse to emit a class file if you tried it. This means that `Annotation > onMethod() default @Deprecated;` isn't treated as illegal by the JVMS > either, and thus the JVMS needs no updates to reflect that this would be a > legal construct now. There is furthermore no commentary about the fact that > the return type of an element of an `ACC_ANNOTATION` type can only be a > primitive, String, Class, an annotation type, or a 1-dimensional array of > those. Therefore, no comment needs to be added to explain that > `java.lang.annotation.Annotation` is also legal even though it is not an > annotation type according to the JLS. > > ### JLS Specification: A few changes required > > _based on [Java Language Specification SE7][JLS]_ > > 1. 9.6 - **Annotation Types** This introductory section describes the > actual declaration of an annotation type; no changes needed. > > 2. 9.6.1 - **Annotation Type Elements** requires two changes: > > _old:_ > > It is a compile-time error if the return type of a method declared in an > annotation > ?type is not one of the following: a primitive type, String, Class, any > parameterized > invocation of Class, an enum type (S8.9), an annotation type, or an array > type > ?(S10) whose element type is one of the preceding types. > > _new:_ > ?It is a compile-time error if the return type of a method declared in an > annotation > ?type is not one of the following: a primitive type, String, Class, any > parameterized > invocation of Class, an enum type (S8.9), an annotation type, > `java.lang.annotation.Annotation`, > ?or an array type (S10) whose element type is one of the preceding types. > ?[smaller] > If the return type of an annotation method is declared to be > `java.lang.annotation.Annotation` (or its array), any annotation (S9.7) is > a valid value: > ?@interface Getter { > Annotation[] onMethod() default {@NonNull}; > ?} > [/smaller] > ?_old:_ > ?It is a compile-time error if an annotation type declaration T contains an > element > ?of type T, either directly or indirectly. > ?[smaller] > ?For example, this is illegal: > ?@interface SelfRef { SelfRef value(); } > ?and so is this: > ?@interface Ping { Pong value(); } > @interface Pong { Ping value(); } > [/smaller] > ?_new (optional change; add an example to clarify):_ > ?[smaller] > For example, this is illegal: > ?@interface SelfRef { SelfRef value(); } > ?and so is this: > ?@interface Ping { Pong value(); } > @interface Pong { Ping value(); } > ?as is this: > ?@interface SelfRef { Annotation value() default @SelfRef; } > [/smaller] > > 3. 9.6.2 - **Defaults for Annotation Type Elements** requires no changes. > The relevant paragraph reads: > > It is a compile-time error if the type of the element is not commensurate > (S9.7) with > the default value specified. > > After applying the required changes to sections S9.6.1 and S9.7.1, this > statement is still valid. > > 4. 9.6.3 - **Predefined Annotation Types** requires no changes. > > 5. 9.7 - **Annotations** requires no changes (this is an introduction > section). > > 6. 9.7.1 - **Normal Annotations** requires one change and one optional > change. > > The production rule for _ElementValue_ does not need changing because it > already mentions it can consist of an _Annotation_. > ?_old_: > ?The return type of this method defines the element type of the > element-value pair. > ?_new_: > ?The return type of this method defines the element type of the > element-value pair. > If the return type is `java.lang.annotation.Annotation`, any annotation is > a valid > ?value. > ?_old_: > ?The type of V is assignment compatible (S5.2) with T, and furthermore: > * If T is a primitive type or String, and V is a constant expression > (S15.28). > ?* V is not null. > * If T is Class, or an invocation of Class, and V is a class literal > (S15.8.2). > ?* If T is an enum type, and V is an enum constant. > ?_append an item to the list as clarification:_ > ?* If T is `java.lang.annotation.Annotation`, and V is an annotation. > > 7. 9.7.2 - **Marker Annotations** only describes a shorthand notation; > needs no changes. > > 8. 9.7.3 - **Single-Element Annotations** only describes a shorthand > notation; needs no changes. > > 9. 13.5.7 - **Evolution of Annotation Types** needs no changes. > > ### Changes to javadoc of existing java.* API: No changes required > > 1. method `java.lang.reflect.Method.getDefaultValue()`'s javadoc does not > mention anything about annotation member's return types being restricted to > a subset of legal types, therefore no update to include > `java.lang.annotation.Annotation` in this subset is required. > > 2. `java.lang.Class.isAnnotation()` (and `getModifiers() & > java.lang.reflect.Modifier.ANNOTATION`) is the only aspect of `Class` which > is different / relevant for annotation types, and it also makes no mention > of the return type limitations. > > 3. Existing annotations in the java base library itself (`@Override`, > `@Deprecated`, etc - listed in JLS sections 9.6.3.1-9.6.3.7) do not have > any methods which are an annotation type, nor do any of these types seem > like they could use one. No updates are required or suggested for any of > them. > > 4. The javadoc on `java.lang.annotation.Annotation` itself remains valid. > It might be prudent to expand it with a section that explains that it can > be used as a return type for an annotation method, but the other legal > return types for annotation declaration members don't have this either. > Therefore, for consistency's sake, this proposal does not include a change > to this javadoc. > > ### Changes / additions to any of the method signatures of the reflection > API or any other part of java base: No changes required > > * `java.lang.reflect.Method.getDefaultValue()` already returns > `java.lang.Object` and thus needs no changes. > > * No new API is required to reflectively determine that a given annotation > declaration member's return type is `Annotation`, because the way this > return type is reflected is via a `java.lang.Class` return type, which is > already capable of conveying `Annotation` as a value. This part is one of > the few ways existing tools might break, as they may erroneously assume > this return value can only be `java.lang.Class.class`, > `java.lang.String.class`, any of the primitive wrappers, or a type which is > an annotation type. This method could now also return > `java.lang.annotation.Annotation` which is not itself an annotation type. > > * No new API is required to reflectively read out annotation values, as > these will still be specific instances of annotations. > > * No new API is required to reflectively read out annotation defaults, as > these, if present, will still be specific instances of annotations. > > ### Changes / additions to JVM library reflection core (java.*): No changes > needed > > 1. `java.lang.reflect.Method.getDefaultValue()` delegates work to internal > implementations and does not contain any code that would cause issues with > a `java.lang.annotation.Annotation` return type. It does first acquire the > return type of the method and then asks for an instance of the value that > 'fits' this return type, but it leaves all checking to the internal > implementations that provide both of these values (both the return type and > an instance for the value given the byte array containing the raw > `annotationDefault` data). The work is deferred to > `sun.reflect.annotation.AnnotationParser` and > `sun.reflection.annotation.AnnotationType`, which do need patches (see > below). > > 2. `java.lang.reflect.Method.getAnnotation(java.lang.Class > annotationClass)` defers work to `sun.reflect.annotation.AnnotationParser` > and `sun.reflect.annotation.AnnotationType` as well. Same for > `java.lang.reflect.Field.getAnnotation`, and > `java.lang.Class.getAnnotation`. `java.lang.Package.getAnnotation` is a > wrapper around a dummy `java.lang.Class` instance that holds annotation > data (and thus, it too defers the work). These internal helpers do need > patches (see below). > > ### Changes / additions to internal support classes used by reflective > core: One tiny change needed to a system assertion > > 1. `sun.reflect.annotation.AnnotationType` - no changes necessary. > > 2. `sun.reflect.annotation.AnnotationInvocationHandler` - no changes > necessary. In particular, the `equals()` implementation does not use the > return type, only member values; all special handling defaults to > `equals()`, `toString()`, `hashCode()` etc of the member value if the > member value is an annotation type. annotation instances already have > working `equals`, `hashCode`, `toString`, etc implementations, therefore no > changes are necessary. > > 3. `sun.reflect.annotation.AnnotationParser` - Minor changes necessary. > > _based on [Revision 9b8c96f96a0f of > AnnotationParser.java][AnnotationParser]_ > > * method `parseMemberValue` does NOT provide the expected type (parameter > `memberType`) to the `parseAnnotation` helper method. Therefore, the fact > that expected type is a previously invalid value > (`java.lang.annotation.Annotation`) does not have any effect on parsing the > annotation in the class file data. > > * method `parseSig` does not check if the provided type is an annotation > type. (It really can't, as the type is not guaranteed to be on the > classpath, and therefore it has no way of knowing if the provided type is > actually an annotation type. Nevertheless, the code does indeed include no > such check). > > * method 'parseArray' (helper of `parseMemberValue`) contains an assertion > (which can be enabled with the `-esa` javac option) on line 485 which needs > updating: > > - assert componentType.isAnnotation(); > ?+ assert componentType.isAnnotation() || componentType == > java.lang.annotation.Annotation.class; > > ### Changes to javac: Some changes needed > > * javac checks that an annotation declaration member's return type is one > of the allowed types. This check needs to be extended to consider > `java.lang.annotation.Annotation` a legal return type value as well. No > change in the way javac builds the class file to represent the annotation > declaration is required: > > In com.sun.tools.javac.comp.Check:2267 > _based on [Revision ce654f4ecfd8 of Check.java][Check]_ > > - if ((type.tsym.flags() & Flags.ANNOTATION) != 0) { > ?+ if ((type.tsym.flags() & Flags.ANNOTATION) != 0 || ?|| > types.isSameType(type, syms.annotationType)) { > > * Loop detection: It is now possible to create 'loops' in default values, > where the default value of an annotation is itself, or, indirectly, some > other annotation, one of whose methods contains a default value that points > back itself. Prior to the introduction of this feature, the rule that the > return types of annotation methods cannot contain a cyclic reference would > make it impossible for the default value to contain such a loop, but now > this is no longer true, so a separate loop detection scheme needs to be > implemented for default values. > > NB: The `checkAnnotationResType` method has been renamed in this patch to > `checkAnnotationElementType` because it is now no longer used just to check > return types, but also to check the types in a `default` value. > > This change is a bit more involved and thus the full patch is listed here > in posix diff format: > > Full patch of com.sun.tools.javac.comp.Check: > _based on [Revision ce654f4ecfd8 of Check.java][Check]_ > > 29d28 > < import java.util.Set; > 33a33,34 > ?> import com.sun.tools.javac.tree.JCTree.JCAnnotation; >> import com.sun.tools.javac.tree.JCTree.JCExpression; > ?38a40 >> import com.sun.tools.javac.code.Attribute.Array; > 40a43 > ?> import com.sun.tools.javac.code.Type; > 2267c2270 > < ? ? ? ? if ((type.tsym.flags() & Flags.ANNOTATION) != 0) return; > ?--- >> ? ? ? ? if ((type.tsym.flags() & Flags.ANNOTATION) != 0 || > types.isSameType(type, syms.annotationType)) return; > ?2497c2500,2501 > < ? ? ? ? ? ? ? ? checkAnnotationResType(meth.pos(), meth.restype.type); > ?--- >> ? ? ? ? ? ? ? ? checkAnnotationElementType(meth.pos(), meth.restype.type); > ?> ? ? ? ? ? ? ? ? checkNonCyclicAnnotationDefaultValues(meth); > 2518c2522,2523 > ?< ? ? ? ? ? ? ? ? checkAnnotationResType(pos, > ((MethodSymbol)s).type.getReturnType()); > --- > ?> ? ? ? ? ? ? ? ? checkAnnotationElementType(pos, > ((MethodSymbol)s).type.getReturnType()); >> ? ? ? ? ? ? ? ? checkNonCyclicAnnotationDefaultValues(pos, > (MethodSymbol)s); > ?2526c2531 > < ? ? void checkAnnotationResType(DiagnosticPosition pos, Type type) { > ?--- >> ? ? void checkAnnotationElementType(DiagnosticPosition pos, Type type) { > ?2533c2538 > < ? ? ? ? ? ? checkAnnotationResType(pos, types.elemtype(type)); > ?--- >> ? ? ? ? ? ? checkAnnotationElementType(pos, types.elemtype(type)); > 2539a2545,2579 > ?> ? ? private void checkNonCyclicAnnotationDefaultValues(JCMethodDecl > meth) { >> ? ? ? ? if (!isAnnotationType(meth.restype.type)) return; > ?> ? ? ? ? if (meth.defaultValue == null) return; >> ? ? ? ? meth.defaultValue.accept(new TreeScanner() { > ?> ? ? ? ? ? ? @Override public void visitAnnotation(JCAnnotation tree) { >> ? ? ? ? ? ? ? ? checkAnnotationElementType(tree.pos(), tree.type); > ?> ? ? ? ? ? ? ? ? super.visitAnnotation(tree); >> ? ? ? ? ? ? } >> ? ? ? ? }); > ?> ? ? } >> >> ? ? private void checkNonCyclicAnnotationDefaultValues(final > DiagnosticPosition pos, MethodSymbol meth) { > ?> ? ? ? ? if (!isAnnotationType(meth.type.getReturnType())) return; >> ? ? ? ? if (meth.defaultValue == null) return; > ?> ? ? ? ? if (meth.defaultValue.type.tag == TypeTags.ARRAY) { >> ? ? ? ? ? ? for (Attribute a : ((Array)meth.defaultValue).values) { > ?> ? ? ? ? ? ? ? ? checkAnnotationElementType(pos, a.type); >> ? ? ? ? ? ? } > ?> ? ? ? ? } >> ? ? ? ? else { >> ? ? ? ? ? ? checkAnnotationElementType(pos, meth.defaultValue.type); > ?> ? ? ? ? } >> ? ? } >> > ?> ? ? private boolean isAnnotationType(Type type) { >> ? ? ? ? switch (type.tag) { > ?> ? ? ? ? case TypeTags.CLASS: >> ? ? ? ? ? ? return types.isSameType(type, syms.annotationType); > ?> ? ? ? ? case TypeTags.ARRAY: >> ? ? ? ? ? ? return types.isSameType(types.elemtype(type), > syms.annotationType); > ?> ? ? ? ? default: >> ? ? ? ? ? ? return false; >> ? ? ? ? } > ?> ? ? } >> > > * The error message with key 'cyclic.annotation.element' doesn't need > changing. > > * javac checks that an annotation's parameter is type compatible with the > annotation's declaration. It does this using an 'assignment compatible' > check, which will work fine with `java.lang.annotation.Annotation` as > return type of the annotation declaration member method. However, this > check is entered using an `if` statement which needs to be expanded: > > In com.sun.tools.javac.comp.Annotate:224 > _based on [Revision ce654f4ecfd8 of Annotate.java][Annotate]_ > > - if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) { > ?+ if ((expected.tsym.flags() & Flags.ANNOTATION) != 0 || > types.isSameType(expected, syms.annotationType)) { > > * No other changes are required. The error message with key > `invalid.annotation.member.type` may need to be expanded to explain that > `Annotation` is also a legal type. > > ### Changes to javax.lang.model: Some changes needed > > * javax.lang.model mostly requires no changes, except for the feature where > one can ask javax.lang.model to create an instance of a given annotation > class, i.e.: > > for (Element elem : roundEnv.getRootElements()) { > SomeAnnotation instanceOfAnnotation = > elem.getAnnotation(SomeAnnotation.class); > ?} > > The implementation of this feature in OpenJDK's javac obtains > `java.lang.Class` instances (needed to create the proxies) entirely from > traversing `SomeAnnotation.class` using reflection. As the return types of > the methods in `SomeAnnotation.class` would be > `java.lang.annotation.Annotation`, this mechanism is no longer useful. > Instead, the 'flat name' of the annotation argument itself needs to be > turned into a `java.lang.Class` by using `Class.forName()`. Also, it is now > possible for an annotation argument to contain an annotation that is not on > the classpath of the annotation processor. The right approach is for this > annotation instance to throw a `TypeMirrorException` as late as is feasible > (when the annotation method is invoked that would have to return an > instance of a type that is not available, but not when i.e. `toString()` is > invoked). A full patch to the appropriate class is listed here (note that > the alternate strategy of using `Class.forName` is only used for the new > feature; existing annotations that do not use it are still created by > traversing the annototation type via reflection): > > In com.sun.tools.javac.model.AnnotationProxyMaker > _based on [Revision ce654f4ecfd8 of > AnnotationProxyMaker.java][AnnotationProxyMaker]_ > > 62c62,64 > ?< ? ? private final Class annoType; > --- >> ? ? private Class annoType; > ?> ? ? private final Class context; >> ? ? private ClassLoader classLoader; > ?66c68 > < ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Class annoType) { > ?--- >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Class annoType, > Class context) { > ?68a71 >> ? ? ? ? this.context = context; > 77,78c80 > ?< ? ? ? ? AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, > annoType); > < ? ? ? ? return annoType.cast(apm.generateAnnotation()); > ?--- >> ? ? ? ? return annoType.cast(generateAnnotationInner(anno, annoType, > annoType, null)); > ?80a83,88 >> ? ? private static Object generateAnnotationInner( >> ? ? ? ? ? ? Attribute.Compound anno, Class > annoType, Class context, ClassLoader classLoader) { > ?> ? ? ? ? AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, > annoType, context); >> ? ? ? ? apm.classLoader = classLoader; > ?> ? ? ? ? return apm.generateAnnotation(); >> ? ? } > 81a90,100 > ?> ? ? private ClassLoader getAnnotationClassLoader() { >> ? ? ? ? if (classLoader == null) { > ?> ? ? ? ? ? ? ClassLoader cl = context.getClassLoader(); >> ? ? ? ? ? ? // Line above Could cause security exception, but > ?> ? ? ? ? ? ? // no other part of javac uses doPrivileged; >> ? ? ? ? ? ? // in particular line 259 of AnnotationParser also doesn't > bother, > ?> ? ? ? ? ? ? // and is in the same boat. >> ? ? ? ? ? ?this.classLoader = cl != null ? cl : > ClassLoader.getSystemClassLoader(); > ?> ? ? ? ? } >> ? ? ? ? return classLoader; >> ? ? } > ?85c104,116 > < ? ? private Annotation generateAnnotation() { > --- > ?> ? ? @SuppressWarnings("unchecked") >> ? ? private Object generateAnnotation() { > ?> ? ? ? ? if (annoType == Annotation.class) { >> ? ? ? ? ? ? try { >> ? ? ? ? ? ? ? ? Class clazz = (Class Annotation>) > ?> > getAnnotationClassLoader().loadClass(anno.type.tsym.flatName().toString()); >> ? ? ? ? ? ? ? ? annoType = clazz; > ?> ? ? ? ? ? ? ? ? return AnnotationParser.annotationForMap(clazz, >> ? ? ? ? ? ? ? ? ? ? ? ? getAllReflectedValues()); > ?> ? ? ? ? ? ? } catch (ClassNotFoundException e) { >> ? ? ? ? ? ? ? ? return new MirroredTypeExceptionProxy(anno.type); > ?> ? ? ? ? ? ? } >> ? ? ? ? } > 170a202 > ?> > 239c271 > < ? ? ? ? ? ? ? ? value = generateAnnotation(c, nested); > ?--- >> ? ? ? ? ? ? ? ? value = generateAnnotationInner(c, nested, context, > classLoader); > > ## Source and binary compatibility > > This proposal introduces no new or changed behaviour for any source code > which is legal today, and no changes to the class file format. Therefore, > existing source files which compile on javac 1.7 are not affected. > > Existing tools also notice no changes for existing source and class files. > However, newly compiled annotation declarations may now start using > `java.lang.annotation.Annotation` as return type for members, and some > existing tools may have assumed the legal set of return values couldn't > change. These tools will need to be updated. > > Technically, it is possible for existing annotations to be 'widened' - any > return types which used to be a specific annotation type can be generalized > to `java.lang.annotation.Annotation`, but this might cause problems with > consumers of this annotation. The same problem occurs for any library > update where signatures are changed, however. > > ## Use cases for this feature > > One obvious use case for annotations is generating code. If this feature is > added to the JDK, it is possible to specify a list of annotations that > should be put in the generated code. For example, an annotation that will > generate a POJO with implementations for `equals`, `hashCode`, et cetera: > > @GeneratePOJO > @AddAnnotations(onClass=@SuppressWarnings("all")) > ?public class StudentTemplate { > @AddAnnotations(onGetter={@NonNull, @javax.persistence.Id}) > ?private int unid; > } > > This feature also takes a step towards allowing hierarchical annotation > definitions (where an annotation extends another annotation type, instead > of `java.lang.annotation.Annotation`. > > ## Testing the feature > > These patches, plus a test suite as well as a way to build a 'live patching > agent' which allows experimenting with these patches, are available here: > > * [github repository](http://github.com/rspilker/jdk-proposal.anyannotation) > - source repository > * [live agent](http://projectlombok.org/anyannotation) - direct download of > the agent which allows immediate experimentation with any javac7. > > [JVMS]: http://docs.oracle.com/javase/7/specs/jvms/JVMS-JavaSE7.pdf > [JLS]: http://docs.oracle.com/javase/7/specs/jls/JLS-JavaSE7.pdf > [AnnotationParser]: > http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/sun/reflect/annotation/AnnotationParser.java > [Check]: > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Check.java > [Annotate]: > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Annotate.java > [AnnotationProxyMaker]: > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java > From reinier at zwitserloot.com Tue Feb 7 02:04:05 2012 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Tue, 7 Feb 2012 11:04:05 +0100 Subject: AnyAnnotation Proposal In-Reply-To: References: Message-ID: Probably, but I was under the impression a JEP can only be posted by a registered contributor or Oracle partner. I am mistaken on that account? --Reinier Zwitserloot On Tue, Feb 7, 2012 at 08:48, Ben Evans wrote: > Hi Reinier, > > Is this the kind of proposal that should really happen as an OpenJDK JEP? > > Thanks, > > Ben > > On Tue, Feb 7, 2012 at 12:40 AM, Reinier Zwitserloot > wrote: > > # The AnyAnnotation feature > > > > ___v1.0___ > > > > Currently, the JLS specifies that only a direct subtype of > > `java.lang.annotation.Annotation`, defined using the `public @interface` > > syntax, is actually an annotation type, and that the return type of any > > member of an annotation declaration can only be an annotation type (or a > > primitive, String, Class, or a 1-dimensional array). It is therefore not > > possible in java 1.7 to create a member of an annotation declaration that > > implies 'any annotation is legal here'. > > > > This proposal addresses the lack of this feature. It also moves java > closer > > towards supporting hierarchical annotations (the ability to have one > > annotation declaration extend something other than > > `java.lang.annotation.Annotation` itself). In a nutshell, the proposed > > changes: > > > > * Allow `java.lang.annotation.Annotation` as a valid return type for > > annotation declaration members (in JDK 1.7 this is not legal). Using this > > class as return type means any annotation is intended to be legal. > > * Update the JLS to reflect this change (no changes to javadoc, > reflection > > core API, and JVM specification needed). > > * Patch an assert statement in the reflection core API, and 2 lines in > > javac, to support the spec. > > * Add a new loop detection scheme to avoid an endless loop in the > `default` > > construction of an annotation declaration member. > > * Expand on the impact this change will have, both in regards to which > > tooling options now become possible and what impact this change can have > on > > existing tools. > > > > ## Authors > > > > * Reinier Zwitserloot (r.zwitserloot at projectlombok.org) > > * Roel Spilker (r.spilker at projectlombok.org) > > * Sander Koning (s.koning at projectlombok.org) > > * Robbert Jan Grootjans (r.grootjans at projectlombok.org) > > > > ## Try it right now! > > > > A special try-it-out javac agent is available here: > > > > [ > > > http://projectlombok.org/anyannotation](http://projectlombok.org/anyannotation) > > > > This agent 'live patches' any javac7 in memory only, so you can start > > experimenting right away with this feature. > > > > ## Changes required in the JDK and associated documentation > > > > ### JVM Specification: Zero changes required > > > > _Based on [Java Virtual Machine Specification SE7][JVMS]_ > > > > 1. For annotations themselves: Section 4.7.16 - **The > > RuntimeVisibleAnnotations attribute** through Section 4.7.19 - **The > > RuntimeInvisibleParameterAnnotations attribute** specify how annotations > > should be encoded in the class file. These sections say the only way to > > store an annotation inside an annotation is as a new annotation block, > and > > such a block _includes_ the type of the annotation. Thus, in > `@Foo(@Bar)`, > > there is always a pointer to the constant pool entry with > > `com.package.Bar`. It is therefore not necessary to rely on the > signatures > > available in the `Foo` type to understand its parameter, and therefore no > > change is necessary to the class file format to support the case where > > `@Foo`'s parameter is of type `Annotation`. > > > > 2. For annotation type declarations, the JVMS basically contains no > > specifications. They are just plain interfaces with a bit set > > (`ACC_ANNOTATION` - see section 4.1 `access_flags`). The only new aspect > > (other than the `ACC_ANNOTATION` bit, which is not affected by this > > proposal) is the classfile encoding of the `default` value feature of an > > annotation declaration member, which is described in section 4.7.20 - > **The > > AnnotationDefault attribute**. The format of this attribute is explained > in > > terms of section 4.7.16's annotation structure, which already supports > this > > proposal without any changes required. There is absolutely no mention > that > > the value of an _AnnotationDefault_ block has to match the type of the > > `method_info`'s return type (that aspect of the annotation spec is > covered > > in the JLS only). In other words, the JVMS doesn't actually consider `int > > foo() default "Hello, World!";` illegal, though javac obviously would > > refuse to emit a class file if you tried it. This means that `Annotation > > onMethod() default @Deprecated;` isn't treated as illegal by the JVMS > > either, and thus the JVMS needs no updates to reflect that this would be > a > > legal construct now. There is furthermore no commentary about the fact > that > > the return type of an element of an `ACC_ANNOTATION` type can only be a > > primitive, String, Class, an annotation type, or a 1-dimensional array of > > those. Therefore, no comment needs to be added to explain that > > `java.lang.annotation.Annotation` is also legal even though it is not an > > annotation type according to the JLS. > > > > ### JLS Specification: A few changes required > > > > _based on [Java Language Specification SE7][JLS]_ > > > > 1. 9.6 - **Annotation Types** This introductory section describes the > > actual declaration of an annotation type; no changes needed. > > > > 2. 9.6.1 - **Annotation Type Elements** requires two changes: > > > > _old:_ > > > > It is a compile-time error if the return type of a method declared in an > > annotation > > type is not one of the following: a primitive type, String, Class, any > > parameterized > > invocation of Class, an enum type (S8.9), an annotation type, or an array > > type > > (S10) whose element type is one of the preceding types. > > > > _new:_ > > It is a compile-time error if the return type of a method declared in an > > annotation > > type is not one of the following: a primitive type, String, Class, any > > parameterized > > invocation of Class, an enum type (S8.9), an annotation type, > > `java.lang.annotation.Annotation`, > > or an array type (S10) whose element type is one of the preceding types. > > [smaller] > > If the return type of an annotation method is declared to be > > `java.lang.annotation.Annotation` (or its array), any annotation (S9.7) > is > > a valid value: > > @interface Getter { > > Annotation[] onMethod() default {@NonNull}; > > } > > [/smaller] > > _old:_ > > It is a compile-time error if an annotation type declaration T contains > an > > element > > of type T, either directly or indirectly. > > [smaller] > > For example, this is illegal: > > @interface SelfRef { SelfRef value(); } > > and so is this: > > @interface Ping { Pong value(); } > > @interface Pong { Ping value(); } > > [/smaller] > > _new (optional change; add an example to clarify):_ > > [smaller] > > For example, this is illegal: > > @interface SelfRef { SelfRef value(); } > > and so is this: > > @interface Ping { Pong value(); } > > @interface Pong { Ping value(); } > > as is this: > > @interface SelfRef { Annotation value() default @SelfRef; } > > [/smaller] > > > > 3. 9.6.2 - **Defaults for Annotation Type Elements** requires no changes. > > The relevant paragraph reads: > > > > It is a compile-time error if the type of the element is not commensurate > > (S9.7) with > > the default value specified. > > > > After applying the required changes to sections S9.6.1 and S9.7.1, this > > statement is still valid. > > > > 4. 9.6.3 - **Predefined Annotation Types** requires no changes. > > > > 5. 9.7 - **Annotations** requires no changes (this is an introduction > > section). > > > > 6. 9.7.1 - **Normal Annotations** requires one change and one optional > > change. > > > > The production rule for _ElementValue_ does not need changing because it > > already mentions it can consist of an _Annotation_. > > _old_: > > The return type of this method defines the element type of the > > element-value pair. > > _new_: > > The return type of this method defines the element type of the > > element-value pair. > > If the return type is `java.lang.annotation.Annotation`, any annotation > is > > a valid > > value. > > _old_: > > The type of V is assignment compatible (S5.2) with T, and furthermore: > > * If T is a primitive type or String, and V is a constant expression > > (S15.28). > > * V is not null. > > * If T is Class, or an invocation of Class, and V is a class literal > > (S15.8.2). > > * If T is an enum type, and V is an enum constant. > > _append an item to the list as clarification:_ > > * If T is `java.lang.annotation.Annotation`, and V is an annotation. > > > > 7. 9.7.2 - **Marker Annotations** only describes a shorthand notation; > > needs no changes. > > > > 8. 9.7.3 - **Single-Element Annotations** only describes a shorthand > > notation; needs no changes. > > > > 9. 13.5.7 - **Evolution of Annotation Types** needs no changes. > > > > ### Changes to javadoc of existing java.* API: No changes required > > > > 1. method `java.lang.reflect.Method.getDefaultValue()`'s javadoc does not > > mention anything about annotation member's return types being restricted > to > > a subset of legal types, therefore no update to include > > `java.lang.annotation.Annotation` in this subset is required. > > > > 2. `java.lang.Class.isAnnotation()` (and `getModifiers() & > > java.lang.reflect.Modifier.ANNOTATION`) is the only aspect of `Class` > which > > is different / relevant for annotation types, and it also makes no > mention > > of the return type limitations. > > > > 3. Existing annotations in the java base library itself (`@Override`, > > `@Deprecated`, etc - listed in JLS sections 9.6.3.1-9.6.3.7) do not have > > any methods which are an annotation type, nor do any of these types seem > > like they could use one. No updates are required or suggested for any of > > them. > > > > 4. The javadoc on `java.lang.annotation.Annotation` itself remains valid. > > It might be prudent to expand it with a section that explains that it can > > be used as a return type for an annotation method, but the other legal > > return types for annotation declaration members don't have this either. > > Therefore, for consistency's sake, this proposal does not include a > change > > to this javadoc. > > > > ### Changes / additions to any of the method signatures of the reflection > > API or any other part of java base: No changes required > > > > * `java.lang.reflect.Method.getDefaultValue()` already returns > > `java.lang.Object` and thus needs no changes. > > > > * No new API is required to reflectively determine that a given > annotation > > declaration member's return type is `Annotation`, because the way this > > return type is reflected is via a `java.lang.Class` return type, which is > > already capable of conveying `Annotation` as a value. This part is one of > > the few ways existing tools might break, as they may erroneously assume > > this return value can only be `java.lang.Class.class`, > > `java.lang.String.class`, any of the primitive wrappers, or a type which > is > > an annotation type. This method could now also return > > `java.lang.annotation.Annotation` which is not itself an annotation type. > > > > * No new API is required to reflectively read out annotation values, as > > these will still be specific instances of annotations. > > > > * No new API is required to reflectively read out annotation defaults, as > > these, if present, will still be specific instances of annotations. > > > > ### Changes / additions to JVM library reflection core (java.*): No > changes > > needed > > > > 1. `java.lang.reflect.Method.getDefaultValue()` delegates work to > internal > > implementations and does not contain any code that would cause issues > with > > a `java.lang.annotation.Annotation` return type. It does first acquire > the > > return type of the method and then asks for an instance of the value that > > 'fits' this return type, but it leaves all checking to the internal > > implementations that provide both of these values (both the return type > and > > an instance for the value given the byte array containing the raw > > `annotationDefault` data). The work is deferred to > > `sun.reflect.annotation.AnnotationParser` and > > `sun.reflection.annotation.AnnotationType`, which do need patches (see > > below). > > > > 2. `java.lang.reflect.Method.getAnnotation(java.lang.Class > > annotationClass)` defers work to > `sun.reflect.annotation.AnnotationParser` > > and `sun.reflect.annotation.AnnotationType` as well. Same for > > `java.lang.reflect.Field.getAnnotation`, and > > `java.lang.Class.getAnnotation`. `java.lang.Package.getAnnotation` is a > > wrapper around a dummy `java.lang.Class` instance that holds annotation > > data (and thus, it too defers the work). These internal helpers do need > > patches (see below). > > > > ### Changes / additions to internal support classes used by reflective > > core: One tiny change needed to a system assertion > > > > 1. `sun.reflect.annotation.AnnotationType` - no changes necessary. > > > > 2. `sun.reflect.annotation.AnnotationInvocationHandler` - no changes > > necessary. In particular, the `equals()` implementation does not use the > > return type, only member values; all special handling defaults to > > `equals()`, `toString()`, `hashCode()` etc of the member value if the > > member value is an annotation type. annotation instances already have > > working `equals`, `hashCode`, `toString`, etc implementations, therefore > no > > changes are necessary. > > > > 3. `sun.reflect.annotation.AnnotationParser` - Minor changes necessary. > > > > _based on [Revision 9b8c96f96a0f of > > AnnotationParser.java][AnnotationParser]_ > > > > * method `parseMemberValue` does NOT provide the expected type (parameter > > `memberType`) to the `parseAnnotation` helper method. Therefore, the fact > > that expected type is a previously invalid value > > (`java.lang.annotation.Annotation`) does not have any effect on parsing > the > > annotation in the class file data. > > > > * method `parseSig` does not check if the provided type is an annotation > > type. (It really can't, as the type is not guaranteed to be on the > > classpath, and therefore it has no way of knowing if the provided type is > > actually an annotation type. Nevertheless, the code does indeed include > no > > such check). > > > > * method 'parseArray' (helper of `parseMemberValue`) contains an > assertion > > (which can be enabled with the `-esa` javac option) on line 485 which > needs > > updating: > > > > - assert componentType.isAnnotation(); > > + assert componentType.isAnnotation() || componentType == > > java.lang.annotation.Annotation.class; > > > > ### Changes to javac: Some changes needed > > > > * javac checks that an annotation declaration member's return type is one > > of the allowed types. This check needs to be extended to consider > > `java.lang.annotation.Annotation` a legal return type value as well. No > > change in the way javac builds the class file to represent the annotation > > declaration is required: > > > > In com.sun.tools.javac.comp.Check:2267 > > _based on [Revision ce654f4ecfd8 of Check.java][Check]_ > > > > - if ((type.tsym.flags() & Flags.ANNOTATION) != 0) { > > + if ((type.tsym.flags() & Flags.ANNOTATION) != 0 || || > > types.isSameType(type, syms.annotationType)) { > > > > * Loop detection: It is now possible to create 'loops' in default values, > > where the default value of an annotation is itself, or, indirectly, some > > other annotation, one of whose methods contains a default value that > points > > back itself. Prior to the introduction of this feature, the rule that the > > return types of annotation methods cannot contain a cyclic reference > would > > make it impossible for the default value to contain such a loop, but now > > this is no longer true, so a separate loop detection scheme needs to be > > implemented for default values. > > > > NB: The `checkAnnotationResType` method has been renamed in this patch to > > `checkAnnotationElementType` because it is now no longer used just to > check > > return types, but also to check the types in a `default` value. > > > > This change is a bit more involved and thus the full patch is listed here > > in posix diff format: > > > > Full patch of com.sun.tools.javac.comp.Check: > > _based on [Revision ce654f4ecfd8 of Check.java][Check]_ > > > > 29d28 > > < import java.util.Set; > > 33a33,34 > > > import com.sun.tools.javac.tree.JCTree.JCAnnotation; > >> import com.sun.tools.javac.tree.JCTree.JCExpression; > > 38a40 > >> import com.sun.tools.javac.code.Attribute.Array; > > 40a43 > > > import com.sun.tools.javac.code.Type; > > 2267c2270 > > < if ((type.tsym.flags() & Flags.ANNOTATION) != 0) return; > > --- > >> if ((type.tsym.flags() & Flags.ANNOTATION) != 0 || > > types.isSameType(type, syms.annotationType)) return; > > 2497c2500,2501 > > < checkAnnotationResType(meth.pos(), meth.restype.type); > > --- > >> checkAnnotationElementType(meth.pos(), > meth.restype.type); > > > checkNonCyclicAnnotationDefaultValues(meth); > > 2518c2522,2523 > > < checkAnnotationResType(pos, > > ((MethodSymbol)s).type.getReturnType()); > > --- > > > checkAnnotationElementType(pos, > > ((MethodSymbol)s).type.getReturnType()); > >> checkNonCyclicAnnotationDefaultValues(pos, > > (MethodSymbol)s); > > 2526c2531 > > < void checkAnnotationResType(DiagnosticPosition pos, Type type) { > > --- > >> void checkAnnotationElementType(DiagnosticPosition pos, Type type) { > > 2533c2538 > > < checkAnnotationResType(pos, types.elemtype(type)); > > --- > >> checkAnnotationElementType(pos, types.elemtype(type)); > > 2539a2545,2579 > > > private void checkNonCyclicAnnotationDefaultValues(JCMethodDecl > > meth) { > >> if (!isAnnotationType(meth.restype.type)) return; > > > if (meth.defaultValue == null) return; > >> meth.defaultValue.accept(new TreeScanner() { > > > @Override public void visitAnnotation(JCAnnotation tree) { > >> checkAnnotationElementType(tree.pos(), tree.type); > > > super.visitAnnotation(tree); > >> } > >> }); > > > } > >> > >> private void checkNonCyclicAnnotationDefaultValues(final > > DiagnosticPosition pos, MethodSymbol meth) { > > > if (!isAnnotationType(meth.type.getReturnType())) return; > >> if (meth.defaultValue == null) return; > > > if (meth.defaultValue.type.tag == TypeTags.ARRAY) { > >> for (Attribute a : ((Array)meth.defaultValue).values) { > > > checkAnnotationElementType(pos, a.type); > >> } > > > } > >> else { > >> checkAnnotationElementType(pos, meth.defaultValue.type); > > > } > >> } > >> > > > private boolean isAnnotationType(Type type) { > >> switch (type.tag) { > > > case TypeTags.CLASS: > >> return types.isSameType(type, syms.annotationType); > > > case TypeTags.ARRAY: > >> return types.isSameType(types.elemtype(type), > > syms.annotationType); > > > default: > >> return false; > >> } > > > } > >> > > > > * The error message with key 'cyclic.annotation.element' doesn't need > > changing. > > > > * javac checks that an annotation's parameter is type compatible with the > > annotation's declaration. It does this using an 'assignment compatible' > > check, which will work fine with `java.lang.annotation.Annotation` as > > return type of the annotation declaration member method. However, this > > check is entered using an `if` statement which needs to be expanded: > > > > In com.sun.tools.javac.comp.Annotate:224 > > _based on [Revision ce654f4ecfd8 of Annotate.java][Annotate]_ > > > > - if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) { > > + if ((expected.tsym.flags() & Flags.ANNOTATION) != 0 || > > types.isSameType(expected, syms.annotationType)) { > > > > * No other changes are required. The error message with key > > `invalid.annotation.member.type` may need to be expanded to explain that > > `Annotation` is also a legal type. > > > > ### Changes to javax.lang.model: Some changes needed > > > > * javax.lang.model mostly requires no changes, except for the feature > where > > one can ask javax.lang.model to create an instance of a given annotation > > class, i.e.: > > > > for (Element elem : roundEnv.getRootElements()) { > > SomeAnnotation instanceOfAnnotation = > > elem.getAnnotation(SomeAnnotation.class); > > } > > > > The implementation of this feature in OpenJDK's javac obtains > > `java.lang.Class` instances (needed to create the proxies) entirely from > > traversing `SomeAnnotation.class` using reflection. As the return types > of > > the methods in `SomeAnnotation.class` would be > > `java.lang.annotation.Annotation`, this mechanism is no longer useful. > > Instead, the 'flat name' of the annotation argument itself needs to be > > turned into a `java.lang.Class` by using `Class.forName()`. Also, it is > now > > possible for an annotation argument to contain an annotation that is not > on > > the classpath of the annotation processor. The right approach is for this > > annotation instance to throw a `TypeMirrorException` as late as is > feasible > > (when the annotation method is invoked that would have to return an > > instance of a type that is not available, but not when i.e. `toString()` > is > > invoked). A full patch to the appropriate class is listed here (note that > > the alternate strategy of using `Class.forName` is only used for the new > > feature; existing annotations that do not use it are still created by > > traversing the annototation type via reflection): > > > > In com.sun.tools.javac.model.AnnotationProxyMaker > > _based on [Revision ce654f4ecfd8 of > > AnnotationProxyMaker.java][AnnotationProxyMaker]_ > > > > 62c62,64 > > < private final Class annoType; > > --- > >> private Class annoType; > > > private final Class context; > >> private ClassLoader classLoader; > > 66c68 > > < Class annoType) > { > > --- > >> Class annoType, > > Class context) { > > 68a71 > >> this.context = context; > > 77,78c80 > > < AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, > > annoType); > > < return annoType.cast(apm.generateAnnotation()); > > --- > >> return annoType.cast(generateAnnotationInner(anno, annoType, > > annoType, null)); > > 80a83,88 > >> private static Object generateAnnotationInner( > >> Attribute.Compound anno, Class > > annoType, Class context, ClassLoader classLoader) { > > > AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, > > annoType, context); > >> apm.classLoader = classLoader; > > > return apm.generateAnnotation(); > >> } > > 81a90,100 > > > private ClassLoader getAnnotationClassLoader() { > >> if (classLoader == null) { > > > ClassLoader cl = context.getClassLoader(); > >> // Line above Could cause security exception, but > > > // no other part of javac uses doPrivileged; > >> // in particular line 259 of AnnotationParser also doesn't > > bother, > > > // and is in the same boat. > >> this.classLoader = cl != null ? cl : > > ClassLoader.getSystemClassLoader(); > > > } > >> return classLoader; > >> } > > 85c104,116 > > < private Annotation generateAnnotation() { > > --- > > > @SuppressWarnings("unchecked") > >> private Object generateAnnotation() { > > > if (annoType == Annotation.class) { > >> try { > >> Class clazz = (Class > Annotation>) > > > > > > getAnnotationClassLoader().loadClass(anno.type.tsym.flatName().toString()); > >> annoType = clazz; > > > return AnnotationParser.annotationForMap(clazz, > >> getAllReflectedValues()); > > > } catch (ClassNotFoundException e) { > >> return new MirroredTypeExceptionProxy(anno.type); > > > } > >> } > > 170a202 > > > > > 239c271 > > < value = generateAnnotation(c, nested); > > --- > >> value = generateAnnotationInner(c, nested, context, > > classLoader); > > > > ## Source and binary compatibility > > > > This proposal introduces no new or changed behaviour for any source code > > which is legal today, and no changes to the class file format. Therefore, > > existing source files which compile on javac 1.7 are not affected. > > > > Existing tools also notice no changes for existing source and class > files. > > However, newly compiled annotation declarations may now start using > > `java.lang.annotation.Annotation` as return type for members, and some > > existing tools may have assumed the legal set of return values couldn't > > change. These tools will need to be updated. > > > > Technically, it is possible for existing annotations to be 'widened' - > any > > return types which used to be a specific annotation type can be > generalized > > to `java.lang.annotation.Annotation`, but this might cause problems with > > consumers of this annotation. The same problem occurs for any library > > update where signatures are changed, however. > > > > ## Use cases for this feature > > > > One obvious use case for annotations is generating code. If this feature > is > > added to the JDK, it is possible to specify a list of annotations that > > should be put in the generated code. For example, an annotation that will > > generate a POJO with implementations for `equals`, `hashCode`, et cetera: > > > > @GeneratePOJO > > @AddAnnotations(onClass=@SuppressWarnings("all")) > > public class StudentTemplate { > > @AddAnnotations(onGetter={@NonNull, @javax.persistence.Id}) > > private int unid; > > } > > > > This feature also takes a step towards allowing hierarchical annotation > > definitions (where an annotation extends another annotation type, instead > > of `java.lang.annotation.Annotation`. > > > > ## Testing the feature > > > > These patches, plus a test suite as well as a way to build a 'live > patching > > agent' which allows experimenting with these patches, are available here: > > > > * [github repository]( > http://github.com/rspilker/jdk-proposal.anyannotation) > > - source repository > > * [live agent](http://projectlombok.org/anyannotation) - direct > download of > > the agent which allows immediate experimentation with any javac7. > > > > [JVMS]: http://docs.oracle.com/javase/7/specs/jvms/JVMS-JavaSE7.pdf > > [JLS]: http://docs.oracle.com/javase/7/specs/jls/JLS-JavaSE7.pdf > > [AnnotationParser]: > > > http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/sun/reflect/annotation/AnnotationParser.java > > [Check]: > > > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Check.java > > [Annotate]: > > > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Annotate.java > > [AnnotationProxyMaker]: > > > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java > > > From david.holmes at oracle.com Tue Feb 7 02:25:28 2012 From: david.holmes at oracle.com (David Holmes) Date: Tue, 07 Feb 2012 20:25:28 +1000 Subject: AnyAnnotation Proposal In-Reply-To: References: Message-ID: <4F30FC18.6030402@oracle.com> On 7/02/2012 8:04 PM, Reinier Zwitserloot wrote: > Probably, but I was under the impression a JEP can only be posted by a > registered contributor or Oracle partner. I am mistaken on that account? JEPs can be posted by any OpenJDK Committer. http://openjdk.java.net/jeps/1 David ----- > --Reinier Zwitserloot > > > > On Tue, Feb 7, 2012 at 08:48, Ben Evanswrote: > >> Hi Reinier, >> >> Is this the kind of proposal that should really happen as an OpenJDK JEP? >> >> Thanks, >> >> Ben >> >> On Tue, Feb 7, 2012 at 12:40 AM, Reinier Zwitserloot >> wrote: >>> # The AnyAnnotation feature >>> >>> ___v1.0___ >>> >>> Currently, the JLS specifies that only a direct subtype of >>> `java.lang.annotation.Annotation`, defined using the `public @interface` >>> syntax, is actually an annotation type, and that the return type of any >>> member of an annotation declaration can only be an annotation type (or a >>> primitive, String, Class, or a 1-dimensional array). It is therefore not >>> possible in java 1.7 to create a member of an annotation declaration that >>> implies 'any annotation is legal here'. >>> >>> This proposal addresses the lack of this feature. It also moves java >> closer >>> towards supporting hierarchical annotations (the ability to have one >>> annotation declaration extend something other than >>> `java.lang.annotation.Annotation` itself). In a nutshell, the proposed >>> changes: >>> >>> * Allow `java.lang.annotation.Annotation` as a valid return type for >>> annotation declaration members (in JDK 1.7 this is not legal). Using this >>> class as return type means any annotation is intended to be legal. >>> * Update the JLS to reflect this change (no changes to javadoc, >> reflection >>> core API, and JVM specification needed). >>> * Patch an assert statement in the reflection core API, and 2 lines in >>> javac, to support the spec. >>> * Add a new loop detection scheme to avoid an endless loop in the >> `default` >>> construction of an annotation declaration member. >>> * Expand on the impact this change will have, both in regards to which >>> tooling options now become possible and what impact this change can have >> on >>> existing tools. >>> >>> ## Authors >>> >>> * Reinier Zwitserloot (r.zwitserloot at projectlombok.org) >>> * Roel Spilker (r.spilker at projectlombok.org) >>> * Sander Koning (s.koning at projectlombok.org) >>> * Robbert Jan Grootjans (r.grootjans at projectlombok.org) >>> >>> ## Try it right now! >>> >>> A special try-it-out javac agent is available here: >>> >>> [ >>> >> http://projectlombok.org/anyannotation](http://projectlombok.org/anyannotation) >>> >>> This agent 'live patches' any javac7 in memory only, so you can start >>> experimenting right away with this feature. >>> >>> ## Changes required in the JDK and associated documentation >>> >>> ### JVM Specification: Zero changes required >>> >>> _Based on [Java Virtual Machine Specification SE7][JVMS]_ >>> >>> 1. For annotations themselves: Section 4.7.16 - **The >>> RuntimeVisibleAnnotations attribute** through Section 4.7.19 - **The >>> RuntimeInvisibleParameterAnnotations attribute** specify how annotations >>> should be encoded in the class file. These sections say the only way to >>> store an annotation inside an annotation is as a new annotation block, >> and >>> such a block _includes_ the type of the annotation. Thus, in >> `@Foo(@Bar)`, >>> there is always a pointer to the constant pool entry with >>> `com.package.Bar`. It is therefore not necessary to rely on the >> signatures >>> available in the `Foo` type to understand its parameter, and therefore no >>> change is necessary to the class file format to support the case where >>> `@Foo`'s parameter is of type `Annotation`. >>> >>> 2. For annotation type declarations, the JVMS basically contains no >>> specifications. They are just plain interfaces with a bit set >>> (`ACC_ANNOTATION` - see section 4.1 `access_flags`). The only new aspect >>> (other than the `ACC_ANNOTATION` bit, which is not affected by this >>> proposal) is the classfile encoding of the `default` value feature of an >>> annotation declaration member, which is described in section 4.7.20 - >> **The >>> AnnotationDefault attribute**. The format of this attribute is explained >> in >>> terms of section 4.7.16's annotation structure, which already supports >> this >>> proposal without any changes required. There is absolutely no mention >> that >>> the value of an _AnnotationDefault_ block has to match the type of the >>> `method_info`'s return type (that aspect of the annotation spec is >> covered >>> in the JLS only). In other words, the JVMS doesn't actually consider `int >>> foo() default "Hello, World!";` illegal, though javac obviously would >>> refuse to emit a class file if you tried it. This means that `Annotation >>> onMethod() default @Deprecated;` isn't treated as illegal by the JVMS >>> either, and thus the JVMS needs no updates to reflect that this would be >> a >>> legal construct now. There is furthermore no commentary about the fact >> that >>> the return type of an element of an `ACC_ANNOTATION` type can only be a >>> primitive, String, Class, an annotation type, or a 1-dimensional array of >>> those. Therefore, no comment needs to be added to explain that >>> `java.lang.annotation.Annotation` is also legal even though it is not an >>> annotation type according to the JLS. >>> >>> ### JLS Specification: A few changes required >>> >>> _based on [Java Language Specification SE7][JLS]_ >>> >>> 1. 9.6 - **Annotation Types** This introductory section describes the >>> actual declaration of an annotation type; no changes needed. >>> >>> 2. 9.6.1 - **Annotation Type Elements** requires two changes: >>> >>> _old:_ >>> >>> It is a compile-time error if the return type of a method declared in an >>> annotation >>> type is not one of the following: a primitive type, String, Class, any >>> parameterized >>> invocation of Class, an enum type (S8.9), an annotation type, or an array >>> type >>> (S10) whose element type is one of the preceding types. >>> >>> _new:_ >>> It is a compile-time error if the return type of a method declared in an >>> annotation >>> type is not one of the following: a primitive type, String, Class, any >>> parameterized >>> invocation of Class, an enum type (S8.9), an annotation type, >>> `java.lang.annotation.Annotation`, >>> or an array type (S10) whose element type is one of the preceding types. >>> [smaller] >>> If the return type of an annotation method is declared to be >>> `java.lang.annotation.Annotation` (or its array), any annotation (S9.7) >> is >>> a valid value: >>> @interface Getter { >>> Annotation[] onMethod() default {@NonNull}; >>> } >>> [/smaller] >>> _old:_ >>> It is a compile-time error if an annotation type declaration T contains >> an >>> element >>> of type T, either directly or indirectly. >>> [smaller] >>> For example, this is illegal: >>> @interface SelfRef { SelfRef value(); } >>> and so is this: >>> @interface Ping { Pong value(); } >>> @interface Pong { Ping value(); } >>> [/smaller] >>> _new (optional change; add an example to clarify):_ >>> [smaller] >>> For example, this is illegal: >>> @interface SelfRef { SelfRef value(); } >>> and so is this: >>> @interface Ping { Pong value(); } >>> @interface Pong { Ping value(); } >>> as is this: >>> @interface SelfRef { Annotation value() default @SelfRef; } >>> [/smaller] >>> >>> 3. 9.6.2 - **Defaults for Annotation Type Elements** requires no changes. >>> The relevant paragraph reads: >>> >>> It is a compile-time error if the type of the element is not commensurate >>> (S9.7) with >>> the default value specified. >>> >>> After applying the required changes to sections S9.6.1 and S9.7.1, this >>> statement is still valid. >>> >>> 4. 9.6.3 - **Predefined Annotation Types** requires no changes. >>> >>> 5. 9.7 - **Annotations** requires no changes (this is an introduction >>> section). >>> >>> 6. 9.7.1 - **Normal Annotations** requires one change and one optional >>> change. >>> >>> The production rule for _ElementValue_ does not need changing because it >>> already mentions it can consist of an _Annotation_. >>> _old_: >>> The return type of this method defines the element type of the >>> element-value pair. >>> _new_: >>> The return type of this method defines the element type of the >>> element-value pair. >>> If the return type is `java.lang.annotation.Annotation`, any annotation >> is >>> a valid >>> value. >>> _old_: >>> The type of V is assignment compatible (S5.2) with T, and furthermore: >>> * If T is a primitive type or String, and V is a constant expression >>> (S15.28). >>> * V is not null. >>> * If T is Class, or an invocation of Class, and V is a class literal >>> (S15.8.2). >>> * If T is an enum type, and V is an enum constant. >>> _append an item to the list as clarification:_ >>> * If T is `java.lang.annotation.Annotation`, and V is an annotation. >>> >>> 7. 9.7.2 - **Marker Annotations** only describes a shorthand notation; >>> needs no changes. >>> >>> 8. 9.7.3 - **Single-Element Annotations** only describes a shorthand >>> notation; needs no changes. >>> >>> 9. 13.5.7 - **Evolution of Annotation Types** needs no changes. >>> >>> ### Changes to javadoc of existing java.* API: No changes required >>> >>> 1. method `java.lang.reflect.Method.getDefaultValue()`'s javadoc does not >>> mention anything about annotation member's return types being restricted >> to >>> a subset of legal types, therefore no update to include >>> `java.lang.annotation.Annotation` in this subset is required. >>> >>> 2. `java.lang.Class.isAnnotation()` (and `getModifiers()& >>> java.lang.reflect.Modifier.ANNOTATION`) is the only aspect of `Class` >> which >>> is different / relevant for annotation types, and it also makes no >> mention >>> of the return type limitations. >>> >>> 3. Existing annotations in the java base library itself (`@Override`, >>> `@Deprecated`, etc - listed in JLS sections 9.6.3.1-9.6.3.7) do not have >>> any methods which are an annotation type, nor do any of these types seem >>> like they could use one. No updates are required or suggested for any of >>> them. >>> >>> 4. The javadoc on `java.lang.annotation.Annotation` itself remains valid. >>> It might be prudent to expand it with a section that explains that it can >>> be used as a return type for an annotation method, but the other legal >>> return types for annotation declaration members don't have this either. >>> Therefore, for consistency's sake, this proposal does not include a >> change >>> to this javadoc. >>> >>> ### Changes / additions to any of the method signatures of the reflection >>> API or any other part of java base: No changes required >>> >>> * `java.lang.reflect.Method.getDefaultValue()` already returns >>> `java.lang.Object` and thus needs no changes. >>> >>> * No new API is required to reflectively determine that a given >> annotation >>> declaration member's return type is `Annotation`, because the way this >>> return type is reflected is via a `java.lang.Class` return type, which is >>> already capable of conveying `Annotation` as a value. This part is one of >>> the few ways existing tools might break, as they may erroneously assume >>> this return value can only be `java.lang.Class.class`, >>> `java.lang.String.class`, any of the primitive wrappers, or a type which >> is >>> an annotation type. This method could now also return >>> `java.lang.annotation.Annotation` which is not itself an annotation type. >>> >>> * No new API is required to reflectively read out annotation values, as >>> these will still be specific instances of annotations. >>> >>> * No new API is required to reflectively read out annotation defaults, as >>> these, if present, will still be specific instances of annotations. >>> >>> ### Changes / additions to JVM library reflection core (java.*): No >> changes >>> needed >>> >>> 1. `java.lang.reflect.Method.getDefaultValue()` delegates work to >> internal >>> implementations and does not contain any code that would cause issues >> with >>> a `java.lang.annotation.Annotation` return type. It does first acquire >> the >>> return type of the method and then asks for an instance of the value that >>> 'fits' this return type, but it leaves all checking to the internal >>> implementations that provide both of these values (both the return type >> and >>> an instance for the value given the byte array containing the raw >>> `annotationDefault` data). The work is deferred to >>> `sun.reflect.annotation.AnnotationParser` and >>> `sun.reflection.annotation.AnnotationType`, which do need patches (see >>> below). >>> >>> 2. `java.lang.reflect.Method.getAnnotation(java.lang.Class >>> annotationClass)` defers work to >> `sun.reflect.annotation.AnnotationParser` >>> and `sun.reflect.annotation.AnnotationType` as well. Same for >>> `java.lang.reflect.Field.getAnnotation`, and >>> `java.lang.Class.getAnnotation`. `java.lang.Package.getAnnotation` is a >>> wrapper around a dummy `java.lang.Class` instance that holds annotation >>> data (and thus, it too defers the work). These internal helpers do need >>> patches (see below). >>> >>> ### Changes / additions to internal support classes used by reflective >>> core: One tiny change needed to a system assertion >>> >>> 1. `sun.reflect.annotation.AnnotationType` - no changes necessary. >>> >>> 2. `sun.reflect.annotation.AnnotationInvocationHandler` - no changes >>> necessary. In particular, the `equals()` implementation does not use the >>> return type, only member values; all special handling defaults to >>> `equals()`, `toString()`, `hashCode()` etc of the member value if the >>> member value is an annotation type. annotation instances already have >>> working `equals`, `hashCode`, `toString`, etc implementations, therefore >> no >>> changes are necessary. >>> >>> 3. `sun.reflect.annotation.AnnotationParser` - Minor changes necessary. >>> >>> _based on [Revision 9b8c96f96a0f of >>> AnnotationParser.java][AnnotationParser]_ >>> >>> * method `parseMemberValue` does NOT provide the expected type (parameter >>> `memberType`) to the `parseAnnotation` helper method. Therefore, the fact >>> that expected type is a previously invalid value >>> (`java.lang.annotation.Annotation`) does not have any effect on parsing >> the >>> annotation in the class file data. >>> >>> * method `parseSig` does not check if the provided type is an annotation >>> type. (It really can't, as the type is not guaranteed to be on the >>> classpath, and therefore it has no way of knowing if the provided type is >>> actually an annotation type. Nevertheless, the code does indeed include >> no >>> such check). >>> >>> * method 'parseArray' (helper of `parseMemberValue`) contains an >> assertion >>> (which can be enabled with the `-esa` javac option) on line 485 which >> needs >>> updating: >>> >>> - assert componentType.isAnnotation(); >>> + assert componentType.isAnnotation() || componentType == >>> java.lang.annotation.Annotation.class; >>> >>> ### Changes to javac: Some changes needed >>> >>> * javac checks that an annotation declaration member's return type is one >>> of the allowed types. This check needs to be extended to consider >>> `java.lang.annotation.Annotation` a legal return type value as well. No >>> change in the way javac builds the class file to represent the annotation >>> declaration is required: >>> >>> In com.sun.tools.javac.comp.Check:2267 >>> _based on [Revision ce654f4ecfd8 of Check.java][Check]_ >>> >>> - if ((type.tsym.flags()& Flags.ANNOTATION) != 0) { >>> + if ((type.tsym.flags()& Flags.ANNOTATION) != 0 || || >>> types.isSameType(type, syms.annotationType)) { >>> >>> * Loop detection: It is now possible to create 'loops' in default values, >>> where the default value of an annotation is itself, or, indirectly, some >>> other annotation, one of whose methods contains a default value that >> points >>> back itself. Prior to the introduction of this feature, the rule that the >>> return types of annotation methods cannot contain a cyclic reference >> would >>> make it impossible for the default value to contain such a loop, but now >>> this is no longer true, so a separate loop detection scheme needs to be >>> implemented for default values. >>> >>> NB: The `checkAnnotationResType` method has been renamed in this patch to >>> `checkAnnotationElementType` because it is now no longer used just to >> check >>> return types, but also to check the types in a `default` value. >>> >>> This change is a bit more involved and thus the full patch is listed here >>> in posix diff format: >>> >>> Full patch of com.sun.tools.javac.comp.Check: >>> _based on [Revision ce654f4ecfd8 of Check.java][Check]_ >>> >>> 29d28 >>> < import java.util.Set; >>> 33a33,34 >>> > import com.sun.tools.javac.tree.JCTree.JCAnnotation; >>>> import com.sun.tools.javac.tree.JCTree.JCExpression; >>> 38a40 >>>> import com.sun.tools.javac.code.Attribute.Array; >>> 40a43 >>> > import com.sun.tools.javac.code.Type; >>> 2267c2270 >>> < if ((type.tsym.flags()& Flags.ANNOTATION) != 0) return; >>> --- >>>> if ((type.tsym.flags()& Flags.ANNOTATION) != 0 || >>> types.isSameType(type, syms.annotationType)) return; >>> 2497c2500,2501 >>> < checkAnnotationResType(meth.pos(), meth.restype.type); >>> --- >>>> checkAnnotationElementType(meth.pos(), >> meth.restype.type); >>> > checkNonCyclicAnnotationDefaultValues(meth); >>> 2518c2522,2523 >>> < checkAnnotationResType(pos, >>> ((MethodSymbol)s).type.getReturnType()); >>> --- >>> > checkAnnotationElementType(pos, >>> ((MethodSymbol)s).type.getReturnType()); >>>> checkNonCyclicAnnotationDefaultValues(pos, >>> (MethodSymbol)s); >>> 2526c2531 >>> < void checkAnnotationResType(DiagnosticPosition pos, Type type) { >>> --- >>>> void checkAnnotationElementType(DiagnosticPosition pos, Type type) { >>> 2533c2538 >>> < checkAnnotationResType(pos, types.elemtype(type)); >>> --- >>>> checkAnnotationElementType(pos, types.elemtype(type)); >>> 2539a2545,2579 >>> > private void checkNonCyclicAnnotationDefaultValues(JCMethodDecl >>> meth) { >>>> if (!isAnnotationType(meth.restype.type)) return; >>> > if (meth.defaultValue == null) return; >>>> meth.defaultValue.accept(new TreeScanner() { >>> > @Override public void visitAnnotation(JCAnnotation tree) { >>>> checkAnnotationElementType(tree.pos(), tree.type); >>> > super.visitAnnotation(tree); >>>> } >>>> }); >>> > } >>>> >>>> private void checkNonCyclicAnnotationDefaultValues(final >>> DiagnosticPosition pos, MethodSymbol meth) { >>> > if (!isAnnotationType(meth.type.getReturnType())) return; >>>> if (meth.defaultValue == null) return; >>> > if (meth.defaultValue.type.tag == TypeTags.ARRAY) { >>>> for (Attribute a : ((Array)meth.defaultValue).values) { >>> > checkAnnotationElementType(pos, a.type); >>>> } >>> > } >>>> else { >>>> checkAnnotationElementType(pos, meth.defaultValue.type); >>> > } >>>> } >>>> >>> > private boolean isAnnotationType(Type type) { >>>> switch (type.tag) { >>> > case TypeTags.CLASS: >>>> return types.isSameType(type, syms.annotationType); >>> > case TypeTags.ARRAY: >>>> return types.isSameType(types.elemtype(type), >>> syms.annotationType); >>> > default: >>>> return false; >>>> } >>> > } >>>> >>> >>> * The error message with key 'cyclic.annotation.element' doesn't need >>> changing. >>> >>> * javac checks that an annotation's parameter is type compatible with the >>> annotation's declaration. It does this using an 'assignment compatible' >>> check, which will work fine with `java.lang.annotation.Annotation` as >>> return type of the annotation declaration member method. However, this >>> check is entered using an `if` statement which needs to be expanded: >>> >>> In com.sun.tools.javac.comp.Annotate:224 >>> _based on [Revision ce654f4ecfd8 of Annotate.java][Annotate]_ >>> >>> - if ((expected.tsym.flags()& Flags.ANNOTATION) != 0) { >>> + if ((expected.tsym.flags()& Flags.ANNOTATION) != 0 || >>> types.isSameType(expected, syms.annotationType)) { >>> >>> * No other changes are required. The error message with key >>> `invalid.annotation.member.type` may need to be expanded to explain that >>> `Annotation` is also a legal type. >>> >>> ### Changes to javax.lang.model: Some changes needed >>> >>> * javax.lang.model mostly requires no changes, except for the feature >> where >>> one can ask javax.lang.model to create an instance of a given annotation >>> class, i.e.: >>> >>> for (Element elem : roundEnv.getRootElements()) { >>> SomeAnnotation instanceOfAnnotation = >>> elem.getAnnotation(SomeAnnotation.class); >>> } >>> >>> The implementation of this feature in OpenJDK's javac obtains >>> `java.lang.Class` instances (needed to create the proxies) entirely from >>> traversing `SomeAnnotation.class` using reflection. As the return types >> of >>> the methods in `SomeAnnotation.class` would be >>> `java.lang.annotation.Annotation`, this mechanism is no longer useful. >>> Instead, the 'flat name' of the annotation argument itself needs to be >>> turned into a `java.lang.Class` by using `Class.forName()`. Also, it is >> now >>> possible for an annotation argument to contain an annotation that is not >> on >>> the classpath of the annotation processor. The right approach is for this >>> annotation instance to throw a `TypeMirrorException` as late as is >> feasible >>> (when the annotation method is invoked that would have to return an >>> instance of a type that is not available, but not when i.e. `toString()` >> is >>> invoked). A full patch to the appropriate class is listed here (note that >>> the alternate strategy of using `Class.forName` is only used for the new >>> feature; existing annotations that do not use it are still created by >>> traversing the annototation type via reflection): >>> >>> In com.sun.tools.javac.model.AnnotationProxyMaker >>> _based on [Revision ce654f4ecfd8 of >>> AnnotationProxyMaker.java][AnnotationProxyMaker]_ >>> >>> 62c62,64 >>> < private final Class annoType; >>> --- >>>> private Class annoType; >>> > private final Class context; >>>> private ClassLoader classLoader; >>> 66c68 >>> < Class annoType) >> { >>> --- >>>> Class annoType, >>> Class context) { >>> 68a71 >>>> this.context = context; >>> 77,78c80 >>> < AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, >>> annoType); >>> < return annoType.cast(apm.generateAnnotation()); >>> --- >>>> return annoType.cast(generateAnnotationInner(anno, annoType, >>> annoType, null)); >>> 80a83,88 >>>> private static Object generateAnnotationInner( >>>> Attribute.Compound anno, Class >>> annoType, Class context, ClassLoader classLoader) { >>> > AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, >>> annoType, context); >>>> apm.classLoader = classLoader; >>> > return apm.generateAnnotation(); >>>> } >>> 81a90,100 >>> > private ClassLoader getAnnotationClassLoader() { >>>> if (classLoader == null) { >>> > ClassLoader cl = context.getClassLoader(); >>>> // Line above Could cause security exception, but >>> > // no other part of javac uses doPrivileged; >>>> // in particular line 259 of AnnotationParser also doesn't >>> bother, >>> > // and is in the same boat. >>>> this.classLoader = cl != null ? cl : >>> ClassLoader.getSystemClassLoader(); >>> > } >>>> return classLoader; >>>> } >>> 85c104,116 >>> < private Annotation generateAnnotation() { >>> --- >>> > @SuppressWarnings("unchecked") >>>> private Object generateAnnotation() { >>> > if (annoType == Annotation.class) { >>>> try { >>>> Class clazz = (Class>> Annotation>) >>> > >>> >> getAnnotationClassLoader().loadClass(anno.type.tsym.flatName().toString()); >>>> annoType = clazz; >>> > return AnnotationParser.annotationForMap(clazz, >>>> getAllReflectedValues()); >>> > } catch (ClassNotFoundException e) { >>>> return new MirroredTypeExceptionProxy(anno.type); >>> > } >>>> } >>> 170a202 >>> > >>> 239c271 >>> < value = generateAnnotation(c, nested); >>> --- >>>> value = generateAnnotationInner(c, nested, context, >>> classLoader); >>> >>> ## Source and binary compatibility >>> >>> This proposal introduces no new or changed behaviour for any source code >>> which is legal today, and no changes to the class file format. Therefore, >>> existing source files which compile on javac 1.7 are not affected. >>> >>> Existing tools also notice no changes for existing source and class >> files. >>> However, newly compiled annotation declarations may now start using >>> `java.lang.annotation.Annotation` as return type for members, and some >>> existing tools may have assumed the legal set of return values couldn't >>> change. These tools will need to be updated. >>> >>> Technically, it is possible for existing annotations to be 'widened' - >> any >>> return types which used to be a specific annotation type can be >> generalized >>> to `java.lang.annotation.Annotation`, but this might cause problems with >>> consumers of this annotation. The same problem occurs for any library >>> update where signatures are changed, however. >>> >>> ## Use cases for this feature >>> >>> One obvious use case for annotations is generating code. If this feature >> is >>> added to the JDK, it is possible to specify a list of annotations that >>> should be put in the generated code. For example, an annotation that will >>> generate a POJO with implementations for `equals`, `hashCode`, et cetera: >>> >>> @GeneratePOJO >>> @AddAnnotations(onClass=@SuppressWarnings("all")) >>> public class StudentTemplate { >>> @AddAnnotations(onGetter={@NonNull, @javax.persistence.Id}) >>> private int unid; >>> } >>> >>> This feature also takes a step towards allowing hierarchical annotation >>> definitions (where an annotation extends another annotation type, instead >>> of `java.lang.annotation.Annotation`. >>> >>> ## Testing the feature >>> >>> These patches, plus a test suite as well as a way to build a 'live >> patching >>> agent' which allows experimenting with these patches, are available here: >>> >>> * [github repository]( >> http://github.com/rspilker/jdk-proposal.anyannotation) >>> - source repository >>> * [live agent](http://projectlombok.org/anyannotation) - direct >> download of >>> the agent which allows immediate experimentation with any javac7. >>> >>> [JVMS]: http://docs.oracle.com/javase/7/specs/jvms/JVMS-JavaSE7.pdf >>> [JLS]: http://docs.oracle.com/javase/7/specs/jls/JLS-JavaSE7.pdf >>> [AnnotationParser]: >>> >> http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/sun/reflect/annotation/AnnotationParser.java >>> [Check]: >>> >> http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Check.java >>> [Annotate]: >>> >> http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Annotate.java >>> [AnnotationProxyMaker]: >>> >> http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java >>> >> > From martijnverburg at gmail.com Tue Feb 7 03:57:33 2012 From: martijnverburg at gmail.com (Martijn Verburg) Date: Tue, 7 Feb 2012 11:57:33 +0000 Subject: What is the appropriate venue for a very small JDK8 proposal that includes complete patches to javac, JDK, and JLS? In-Reply-To: References: Message-ID: I'd guess that this is still the right place initially, but that it should be submitted as a JEP. Cheers, Martijn On 6 February 2012 23:39, Reinier Zwitserloot wrote: > I'll post a complete proposal for the 'Any Annotation' feature in just a > second, but as coin isn't really happening for JDK8 I'm guessing this > mailing list is not the right venue. Where should I post this? > > ?--Reinier Zwitserloot > From joe.darcy at oracle.com Tue Feb 7 16:12:15 2012 From: joe.darcy at oracle.com (Joe Darcy) Date: Tue, 07 Feb 2012 16:12:15 -0800 Subject: What is the appropriate venue for a very small JDK8 proposal that includes complete patches to javac, JDK, and JLS? In-Reply-To: References: Message-ID: <4F31BDDF.3090506@oracle.com> Yes, the formal procedure for this category of change is now to submit a JEP [1]. JEPs have already been published for various language-related changes planned for JDK 8: JEP 104: Annotations on Java Types (JSR 308) http://openjdk.java.net/jeps/104 JEP 118: Access to Parameter Names at Runtime http://openjdk.java.net/jeps/118 JEP 120: Repeating Annotations http://openjdk.java.net/jeps/120 JEP 126: Lambda Expressions and Virtual Extension Methods http://openjdk.java.net/jeps/126 Cheers, -Joe [1] http://openjdk.java.net/jeps/1 On 02/07/2012 03:57 AM, Martijn Verburg wrote: > I'd guess that this is still the right place initially, but that it > should be submitted as a JEP. > > Cheers, > Martijn > > On 6 February 2012 23:39, Reinier Zwitserloot wrote: >> I'll post a complete proposal for the 'Any Annotation' feature in just a >> second, but as coin isn't really happening for JDK8 I'm guessing this >> mailing list is not the right venue. Where should I post this? >> >> --Reinier Zwitserloot >> From joe.darcy at oracle.com Tue Feb 7 16:17:31 2012 From: joe.darcy at oracle.com (Joe Darcy) Date: Tue, 07 Feb 2012 16:17:31 -0800 Subject: AnyAnnotation Proposal In-Reply-To: References: Message-ID: <4F31BF1B.4000707@oracle.com> Thanks for sending this in Reinier. From skimming the proposal, you and your team seems to have responsibly looked at the major areas potentially impacted by such a change. As previously covered on this list, sending in a JEP is the official way to proceed to try to get this into JDK 8. Due to other in-progress work, I won't be able to offer an analysis of the AnyAnnotation feature to coin-dev for a while. Cheers, -Joe On 02/06/2012 03:40 PM, Reinier Zwitserloot wrote: > # The AnyAnnotation feature > > ___v1.0___ > > Currently, the JLS specifies that only a direct subtype of > `java.lang.annotation.Annotation`, defined using the `public @interface` > syntax, is actually an annotation type, and that the return type of any > member of an annotation declaration can only be an annotation type (or a > primitive, String, Class, or a 1-dimensional array). It is therefore not > possible in java 1.7 to create a member of an annotation declaration that > implies 'any annotation is legal here'. > > This proposal addresses the lack of this feature. It also moves java closer > towards supporting hierarchical annotations (the ability to have one > annotation declaration extend something other than > `java.lang.annotation.Annotation` itself). In a nutshell, the proposed > changes: > > * Allow `java.lang.annotation.Annotation` as a valid return type for > annotation declaration members (in JDK 1.7 this is not legal). Using this > class as return type means any annotation is intended to be legal. > * Update the JLS to reflect this change (no changes to javadoc, reflection > core API, and JVM specification needed). > * Patch an assert statement in the reflection core API, and 2 lines in > javac, to support the spec. > * Add a new loop detection scheme to avoid an endless loop in the `default` > construction of an annotation declaration member. > * Expand on the impact this change will have, both in regards to which > tooling options now become possible and what impact this change can have on > existing tools. > > ## Authors > > * Reinier Zwitserloot (r.zwitserloot at projectlombok.org) > * Roel Spilker (r.spilker at projectlombok.org) > * Sander Koning (s.koning at projectlombok.org) > * Robbert Jan Grootjans (r.grootjans at projectlombok.org) > > ## Try it right now! > > A special try-it-out javac agent is available here: > > [ > http://projectlombok.org/anyannotation](http://projectlombok.org/anyannotation) > > This agent 'live patches' any javac7 in memory only, so you can start > experimenting right away with this feature. > > ## Changes required in the JDK and associated documentation > > ### JVM Specification: Zero changes required > > _Based on [Java Virtual Machine Specification SE7][JVMS]_ > > 1. For annotations themselves: Section 4.7.16 - **The > RuntimeVisibleAnnotations attribute** through Section 4.7.19 - **The > RuntimeInvisibleParameterAnnotations attribute** specify how annotations > should be encoded in the class file. These sections say the only way to > store an annotation inside an annotation is as a new annotation block, and > such a block _includes_ the type of the annotation. Thus, in `@Foo(@Bar)`, > there is always a pointer to the constant pool entry with > `com.package.Bar`. It is therefore not necessary to rely on the signatures > available in the `Foo` type to understand its parameter, and therefore no > change is necessary to the class file format to support the case where > `@Foo`'s parameter is of type `Annotation`. > > 2. For annotation type declarations, the JVMS basically contains no > specifications. They are just plain interfaces with a bit set > (`ACC_ANNOTATION` - see section 4.1 `access_flags`). The only new aspect > (other than the `ACC_ANNOTATION` bit, which is not affected by this > proposal) is the classfile encoding of the `default` value feature of an > annotation declaration member, which is described in section 4.7.20 - **The > AnnotationDefault attribute**. The format of this attribute is explained in > terms of section 4.7.16's annotation structure, which already supports this > proposal without any changes required. There is absolutely no mention that > the value of an _AnnotationDefault_ block has to match the type of the > `method_info`'s return type (that aspect of the annotation spec is covered > in the JLS only). In other words, the JVMS doesn't actually consider `int > foo() default "Hello, World!";` illegal, though javac obviously would > refuse to emit a class file if you tried it. This means that `Annotation > onMethod() default @Deprecated;` isn't treated as illegal by the JVMS > either, and thus the JVMS needs no updates to reflect that this would be a > legal construct now. There is furthermore no commentary about the fact that > the return type of an element of an `ACC_ANNOTATION` type can only be a > primitive, String, Class, an annotation type, or a 1-dimensional array of > those. Therefore, no comment needs to be added to explain that > `java.lang.annotation.Annotation` is also legal even though it is not an > annotation type according to the JLS. > > ### JLS Specification: A few changes required > > _based on [Java Language Specification SE7][JLS]_ > > 1. 9.6 - **Annotation Types** This introductory section describes the > actual declaration of an annotation type; no changes needed. > > 2. 9.6.1 - **Annotation Type Elements** requires two changes: > > _old:_ > > It is a compile-time error if the return type of a method declared in an > annotation > type is not one of the following: a primitive type, String, Class, any > parameterized > invocation of Class, an enum type (S8.9), an annotation type, or an array > type > (S10) whose element type is one of the preceding types. > > _new:_ > It is a compile-time error if the return type of a method declared in an > annotation > type is not one of the following: a primitive type, String, Class, any > parameterized > invocation of Class, an enum type (S8.9), an annotation type, > `java.lang.annotation.Annotation`, > or an array type (S10) whose element type is one of the preceding types. > [smaller] > If the return type of an annotation method is declared to be > `java.lang.annotation.Annotation` (or its array), any annotation (S9.7) is > a valid value: > @interface Getter { > Annotation[] onMethod() default {@NonNull}; > } > [/smaller] > _old:_ > It is a compile-time error if an annotation type declaration T contains an > element > of type T, either directly or indirectly. > [smaller] > For example, this is illegal: > @interface SelfRef { SelfRef value(); } > and so is this: > @interface Ping { Pong value(); } > @interface Pong { Ping value(); } > [/smaller] > _new (optional change; add an example to clarify):_ > [smaller] > For example, this is illegal: > @interface SelfRef { SelfRef value(); } > and so is this: > @interface Ping { Pong value(); } > @interface Pong { Ping value(); } > as is this: > @interface SelfRef { Annotation value() default @SelfRef; } > [/smaller] > > 3. 9.6.2 - **Defaults for Annotation Type Elements** requires no changes. > The relevant paragraph reads: > > It is a compile-time error if the type of the element is not commensurate > (S9.7) with > the default value specified. > > After applying the required changes to sections S9.6.1 and S9.7.1, this > statement is still valid. > > 4. 9.6.3 - **Predefined Annotation Types** requires no changes. > > 5. 9.7 - **Annotations** requires no changes (this is an introduction > section). > > 6. 9.7.1 - **Normal Annotations** requires one change and one optional > change. > > The production rule for _ElementValue_ does not need changing because it > already mentions it can consist of an _Annotation_. > _old_: > The return type of this method defines the element type of the > element-value pair. > _new_: > The return type of this method defines the element type of the > element-value pair. > If the return type is `java.lang.annotation.Annotation`, any annotation is > a valid > value. > _old_: > The type of V is assignment compatible (S5.2) with T, and furthermore: > * If T is a primitive type or String, and V is a constant expression > (S15.28). > * V is not null. > * If T is Class, or an invocation of Class, and V is a class literal > (S15.8.2). > * If T is an enum type, and V is an enum constant. > _append an item to the list as clarification:_ > * If T is `java.lang.annotation.Annotation`, and V is an annotation. > > 7. 9.7.2 - **Marker Annotations** only describes a shorthand notation; > needs no changes. > > 8. 9.7.3 - **Single-Element Annotations** only describes a shorthand > notation; needs no changes. > > 9. 13.5.7 - **Evolution of Annotation Types** needs no changes. > > ### Changes to javadoc of existing java.* API: No changes required > > 1. method `java.lang.reflect.Method.getDefaultValue()`'s javadoc does not > mention anything about annotation member's return types being restricted to > a subset of legal types, therefore no update to include > `java.lang.annotation.Annotation` in this subset is required. > > 2. `java.lang.Class.isAnnotation()` (and `getModifiers()& > java.lang.reflect.Modifier.ANNOTATION`) is the only aspect of `Class` which > is different / relevant for annotation types, and it also makes no mention > of the return type limitations. > > 3. Existing annotations in the java base library itself (`@Override`, > `@Deprecated`, etc - listed in JLS sections 9.6.3.1-9.6.3.7) do not have > any methods which are an annotation type, nor do any of these types seem > like they could use one. No updates are required or suggested for any of > them. > > 4. The javadoc on `java.lang.annotation.Annotation` itself remains valid. > It might be prudent to expand it with a section that explains that it can > be used as a return type for an annotation method, but the other legal > return types for annotation declaration members don't have this either. > Therefore, for consistency's sake, this proposal does not include a change > to this javadoc. > > ### Changes / additions to any of the method signatures of the reflection > API or any other part of java base: No changes required > > * `java.lang.reflect.Method.getDefaultValue()` already returns > `java.lang.Object` and thus needs no changes. > > * No new API is required to reflectively determine that a given annotation > declaration member's return type is `Annotation`, because the way this > return type is reflected is via a `java.lang.Class` return type, which is > already capable of conveying `Annotation` as a value. This part is one of > the few ways existing tools might break, as they may erroneously assume > this return value can only be `java.lang.Class.class`, > `java.lang.String.class`, any of the primitive wrappers, or a type which is > an annotation type. This method could now also return > `java.lang.annotation.Annotation` which is not itself an annotation type. > > * No new API is required to reflectively read out annotation values, as > these will still be specific instances of annotations. > > * No new API is required to reflectively read out annotation defaults, as > these, if present, will still be specific instances of annotations. > > ### Changes / additions to JVM library reflection core (java.*): No changes > needed > > 1. `java.lang.reflect.Method.getDefaultValue()` delegates work to internal > implementations and does not contain any code that would cause issues with > a `java.lang.annotation.Annotation` return type. It does first acquire the > return type of the method and then asks for an instance of the value that > 'fits' this return type, but it leaves all checking to the internal > implementations that provide both of these values (both the return type and > an instance for the value given the byte array containing the raw > `annotationDefault` data). The work is deferred to > `sun.reflect.annotation.AnnotationParser` and > `sun.reflection.annotation.AnnotationType`, which do need patches (see > below). > > 2. `java.lang.reflect.Method.getAnnotation(java.lang.Class > annotationClass)` defers work to `sun.reflect.annotation.AnnotationParser` > and `sun.reflect.annotation.AnnotationType` as well. Same for > `java.lang.reflect.Field.getAnnotation`, and > `java.lang.Class.getAnnotation`. `java.lang.Package.getAnnotation` is a > wrapper around a dummy `java.lang.Class` instance that holds annotation > data (and thus, it too defers the work). These internal helpers do need > patches (see below). > > ### Changes / additions to internal support classes used by reflective > core: One tiny change needed to a system assertion > > 1. `sun.reflect.annotation.AnnotationType` - no changes necessary. > > 2. `sun.reflect.annotation.AnnotationInvocationHandler` - no changes > necessary. In particular, the `equals()` implementation does not use the > return type, only member values; all special handling defaults to > `equals()`, `toString()`, `hashCode()` etc of the member value if the > member value is an annotation type. annotation instances already have > working `equals`, `hashCode`, `toString`, etc implementations, therefore no > changes are necessary. > > 3. `sun.reflect.annotation.AnnotationParser` - Minor changes necessary. > > _based on [Revision 9b8c96f96a0f of > AnnotationParser.java][AnnotationParser]_ > > * method `parseMemberValue` does NOT provide the expected type (parameter > `memberType`) to the `parseAnnotation` helper method. Therefore, the fact > that expected type is a previously invalid value > (`java.lang.annotation.Annotation`) does not have any effect on parsing the > annotation in the class file data. > > * method `parseSig` does not check if the provided type is an annotation > type. (It really can't, as the type is not guaranteed to be on the > classpath, and therefore it has no way of knowing if the provided type is > actually an annotation type. Nevertheless, the code does indeed include no > such check). > > * method 'parseArray' (helper of `parseMemberValue`) contains an assertion > (which can be enabled with the `-esa` javac option) on line 485 which needs > updating: > > - assert componentType.isAnnotation(); > + assert componentType.isAnnotation() || componentType == > java.lang.annotation.Annotation.class; > > ### Changes to javac: Some changes needed > > * javac checks that an annotation declaration member's return type is one > of the allowed types. This check needs to be extended to consider > `java.lang.annotation.Annotation` a legal return type value as well. No > change in the way javac builds the class file to represent the annotation > declaration is required: > > In com.sun.tools.javac.comp.Check:2267 > _based on [Revision ce654f4ecfd8 of Check.java][Check]_ > > - if ((type.tsym.flags()& Flags.ANNOTATION) != 0) { > + if ((type.tsym.flags()& Flags.ANNOTATION) != 0 || || > types.isSameType(type, syms.annotationType)) { > > * Loop detection: It is now possible to create 'loops' in default values, > where the default value of an annotation is itself, or, indirectly, some > other annotation, one of whose methods contains a default value that points > back itself. Prior to the introduction of this feature, the rule that the > return types of annotation methods cannot contain a cyclic reference would > make it impossible for the default value to contain such a loop, but now > this is no longer true, so a separate loop detection scheme needs to be > implemented for default values. > > NB: The `checkAnnotationResType` method has been renamed in this patch to > `checkAnnotationElementType` because it is now no longer used just to check > return types, but also to check the types in a `default` value. > > This change is a bit more involved and thus the full patch is listed here > in posix diff format: > > Full patch of com.sun.tools.javac.comp.Check: > _based on [Revision ce654f4ecfd8 of Check.java][Check]_ > > 29d28 > < import java.util.Set; > 33a33,34 > > import com.sun.tools.javac.tree.JCTree.JCAnnotation; >> import com.sun.tools.javac.tree.JCTree.JCExpression; > 38a40 >> import com.sun.tools.javac.code.Attribute.Array; > 40a43 > > import com.sun.tools.javac.code.Type; > 2267c2270 > < if ((type.tsym.flags()& Flags.ANNOTATION) != 0) return; > --- >> if ((type.tsym.flags()& Flags.ANNOTATION) != 0 || > types.isSameType(type, syms.annotationType)) return; > 2497c2500,2501 > < checkAnnotationResType(meth.pos(), meth.restype.type); > --- >> checkAnnotationElementType(meth.pos(), meth.restype.type); > > checkNonCyclicAnnotationDefaultValues(meth); > 2518c2522,2523 > < checkAnnotationResType(pos, > ((MethodSymbol)s).type.getReturnType()); > --- > > checkAnnotationElementType(pos, > ((MethodSymbol)s).type.getReturnType()); >> checkNonCyclicAnnotationDefaultValues(pos, > (MethodSymbol)s); > 2526c2531 > < void checkAnnotationResType(DiagnosticPosition pos, Type type) { > --- >> void checkAnnotationElementType(DiagnosticPosition pos, Type type) { > 2533c2538 > < checkAnnotationResType(pos, types.elemtype(type)); > --- >> checkAnnotationElementType(pos, types.elemtype(type)); > 2539a2545,2579 > > private void checkNonCyclicAnnotationDefaultValues(JCMethodDecl > meth) { >> if (!isAnnotationType(meth.restype.type)) return; > > if (meth.defaultValue == null) return; >> meth.defaultValue.accept(new TreeScanner() { > > @Override public void visitAnnotation(JCAnnotation tree) { >> checkAnnotationElementType(tree.pos(), tree.type); > > super.visitAnnotation(tree); >> } >> }); > > } >> private void checkNonCyclicAnnotationDefaultValues(final > DiagnosticPosition pos, MethodSymbol meth) { > > if (!isAnnotationType(meth.type.getReturnType())) return; >> if (meth.defaultValue == null) return; > > if (meth.defaultValue.type.tag == TypeTags.ARRAY) { >> for (Attribute a : ((Array)meth.defaultValue).values) { > > checkAnnotationElementType(pos, a.type); >> } > > } >> else { >> checkAnnotationElementType(pos, meth.defaultValue.type); > > } >> } >> > > private boolean isAnnotationType(Type type) { >> switch (type.tag) { > > case TypeTags.CLASS: >> return types.isSameType(type, syms.annotationType); > > case TypeTags.ARRAY: >> return types.isSameType(types.elemtype(type), > syms.annotationType); > > default: >> return false; >> } > > } > * The error message with key 'cyclic.annotation.element' doesn't need > changing. > > * javac checks that an annotation's parameter is type compatible with the > annotation's declaration. It does this using an 'assignment compatible' > check, which will work fine with `java.lang.annotation.Annotation` as > return type of the annotation declaration member method. However, this > check is entered using an `if` statement which needs to be expanded: > > In com.sun.tools.javac.comp.Annotate:224 > _based on [Revision ce654f4ecfd8 of Annotate.java][Annotate]_ > > - if ((expected.tsym.flags()& Flags.ANNOTATION) != 0) { > + if ((expected.tsym.flags()& Flags.ANNOTATION) != 0 || > types.isSameType(expected, syms.annotationType)) { > > * No other changes are required. The error message with key > `invalid.annotation.member.type` may need to be expanded to explain that > `Annotation` is also a legal type. > > ### Changes to javax.lang.model: Some changes needed > > * javax.lang.model mostly requires no changes, except for the feature where > one can ask javax.lang.model to create an instance of a given annotation > class, i.e.: > > for (Element elem : roundEnv.getRootElements()) { > SomeAnnotation instanceOfAnnotation = > elem.getAnnotation(SomeAnnotation.class); > } > > The implementation of this feature in OpenJDK's javac obtains > `java.lang.Class` instances (needed to create the proxies) entirely from > traversing `SomeAnnotation.class` using reflection. As the return types of > the methods in `SomeAnnotation.class` would be > `java.lang.annotation.Annotation`, this mechanism is no longer useful. > Instead, the 'flat name' of the annotation argument itself needs to be > turned into a `java.lang.Class` by using `Class.forName()`. Also, it is now > possible for an annotation argument to contain an annotation that is not on > the classpath of the annotation processor. The right approach is for this > annotation instance to throw a `TypeMirrorException` as late as is feasible > (when the annotation method is invoked that would have to return an > instance of a type that is not available, but not when i.e. `toString()` is > invoked). A full patch to the appropriate class is listed here (note that > the alternate strategy of using `Class.forName` is only used for the new > feature; existing annotations that do not use it are still created by > traversing the annototation type via reflection): > > In com.sun.tools.javac.model.AnnotationProxyMaker > _based on [Revision ce654f4ecfd8 of > AnnotationProxyMaker.java][AnnotationProxyMaker]_ > > 62c62,64 > < private final Class annoType; > --- >> private Class annoType; > > private final Class context; >> private ClassLoader classLoader; > 66c68 > < Class annoType) { > --- >> Class annoType, > Class context) { > 68a71 >> this.context = context; > 77,78c80 > < AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, > annoType); > < return annoType.cast(apm.generateAnnotation()); > --- >> return annoType.cast(generateAnnotationInner(anno, annoType, > annoType, null)); > 80a83,88 >> private static Object generateAnnotationInner( >> Attribute.Compound anno, Class > annoType, Class context, ClassLoader classLoader) { > > AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, > annoType, context); >> apm.classLoader = classLoader; > > return apm.generateAnnotation(); >> } > 81a90,100 > > private ClassLoader getAnnotationClassLoader() { >> if (classLoader == null) { > > ClassLoader cl = context.getClassLoader(); >> // Line above Could cause security exception, but > > // no other part of javac uses doPrivileged; >> // in particular line 259 of AnnotationParser also doesn't > bother, > > // and is in the same boat. >> this.classLoader = cl != null ? cl : > ClassLoader.getSystemClassLoader(); > > } >> return classLoader; >> } > 85c104,116 > < private Annotation generateAnnotation() { > --- > > @SuppressWarnings("unchecked") >> private Object generateAnnotation() { > > if (annoType == Annotation.class) { >> try { >> Class clazz = (Class Annotation>) > > > getAnnotationClassLoader().loadClass(anno.type.tsym.flatName().toString()); >> annoType = clazz; > > return AnnotationParser.annotationForMap(clazz, >> getAllReflectedValues()); > > } catch (ClassNotFoundException e) { >> return new MirroredTypeExceptionProxy(anno.type); > > } >> } > 170a202 > > > 239c271 > < value = generateAnnotation(c, nested); > --- >> value = generateAnnotationInner(c, nested, context, > classLoader); > > ## Source and binary compatibility > > This proposal introduces no new or changed behaviour for any source code > which is legal today, and no changes to the class file format. Therefore, > existing source files which compile on javac 1.7 are not affected. > > Existing tools also notice no changes for existing source and class files. > However, newly compiled annotation declarations may now start using > `java.lang.annotation.Annotation` as return type for members, and some > existing tools may have assumed the legal set of return values couldn't > change. These tools will need to be updated. > > Technically, it is possible for existing annotations to be 'widened' - any > return types which used to be a specific annotation type can be generalized > to `java.lang.annotation.Annotation`, but this might cause problems with > consumers of this annotation. The same problem occurs for any library > update where signatures are changed, however. > > ## Use cases for this feature > > One obvious use case for annotations is generating code. If this feature is > added to the JDK, it is possible to specify a list of annotations that > should be put in the generated code. For example, an annotation that will > generate a POJO with implementations for `equals`, `hashCode`, et cetera: > > @GeneratePOJO > @AddAnnotations(onClass=@SuppressWarnings("all")) > public class StudentTemplate { > @AddAnnotations(onGetter={@NonNull, @javax.persistence.Id}) > private int unid; > } > > This feature also takes a step towards allowing hierarchical annotation > definitions (where an annotation extends another annotation type, instead > of `java.lang.annotation.Annotation`. > > ## Testing the feature > > These patches, plus a test suite as well as a way to build a 'live patching > agent' which allows experimenting with these patches, are available here: > > * [github repository](http://github.com/rspilker/jdk-proposal.anyannotation) > - source repository > * [live agent](http://projectlombok.org/anyannotation) - direct download of > the agent which allows immediate experimentation with any javac7. > > [JVMS]: http://docs.oracle.com/javase/7/specs/jvms/JVMS-JavaSE7.pdf > [JLS]: http://docs.oracle.com/javase/7/specs/jls/JLS-JavaSE7.pdf > [AnnotationParser]: > http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/sun/reflect/annotation/AnnotationParser.java > [Check]: > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Check.java > [Annotate]: > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/comp/Annotate.java > [AnnotationProxyMaker]: > http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java >