Type annotations in static initializers
Werner Dietl
wdietl at gmail.com
Sat Feb 23 19:23:58 PST 2013
Jon, Steve, all,
after spending some time with my dear friend System.out.println it
looks to me as if the MethodSymbol used for Attribution doesn't get
used anywhere else.
I've now introduced two new fields in ClassSymbol to store init and
clinit type annotations.
I've also re-shuffled type annotations from the field symbol to either
the constructors or a clinit method.
Have a look here:
http://hg.openjdk.java.net/type-annotations/type-annotations/langtools/rev/168d80958b13
I have some basic tests in
typeAnnotations/referenceinfos/Initializers.java
but as mentioned before, a different kind of testing would be needed for this.
Also, I fixed a bug with lazy constant value attribution and I think
now no type annotation with UNKNOWN position should reach the
classwriter.
This interaction should also be tested more extensively.
All comments and failing tests very welcome.
Cheers,
cu, WMD.
On Thu, Feb 21, 2013 at 5:58 PM, Werner Dietl <wdietl at gmail.com> wrote:
> Thanks for that clarification, Alex!
> The current implementation takes the "smallest enclosing class, field,
> method, or Code structure" part to apply to the source level and then
> puts annotations from the initializer on the field.
> I agree that this behavior should be changed and will do it together
> with init/clinit re-shuffling.
>
> Steve/SQE, please think about how to test for correct behavior. It
> would be great if you could make failing test cases for this that I
> will then work on fixing.
>
> Thanks,
> cu, WMD.
>
> On Thu, Feb 21, 2013 at 4:19 PM, Alex Buckley <alex.buckley at oracle.com> wrote:
>> An annotation on a field declaration was traditionally stored as an
>> attribute of the field_info structure. The JSR 308 spec reuses that behavior
>> for an annotation on a field type.
>>
>> But this is moot because the Test class has no annotations on the
>> declaration or type of field f. The Test class has an annotation on a type
>> in an expression. Then, the germane line from the 308 spec is:
>>
>> "A type annotation is stored in a Runtime[In]visibleTypeAnnotations
>> attribute on the smallest enclosing class, field, method, or Code
>> structure."
>>
>> So @TA must be stored as a target_type==NEW attribute in the Code attribute
>> of the class file's method which implements the instance variable
>> initializer for f.
>>
>> If you do that, there is no question over the offset value in
>> Runtime[In]VisibleTypeAnnotations. The fact that multiple <init> methods
>> exist is not important, because they will all ultimately cause the instance
>> variable initializer code for f to be run (JLS 12.5).
>>
>> Alex
>>
>>
>> On 2/20/2013 11:57 PM, Werner Dietl wrote:
>>>
>>> Also, to make sure we see the translation equally (Alex, please chime
>>> in if the spec says more on this):
>>>
>>> class Test {
>>> Object f = new @TA Object();
>>> }
>>>
>>> Partial output of javap -v is:
>>>
>>> java.lang.Object f;
>>> descriptor: Ljava/lang/Object;
>>> flags:
>>> RuntimeInvisibleTypeAnnotations:
>>> 0: #8(): NEW, offset=5
>>>
>>> Test();
>>> descriptor: ()V
>>> flags:
>>> Code:
>>> stack=3, locals=2, args_size=1
>>> 0: aload_0
>>> 1: invokespecial #1 // Method
>>> java/lang/Object."<init>":()V
>>> 4: aload_0
>>> 5: new #2 // class java/lang/Object
>>> 8: dup
>>> 9: invokespecial #1 // Method
>>> java/lang/Object."<init>":()V
>>> 12: putfield #3 // Field
>>> f:Ljava/lang/Object;
>>>
>>> So the NEW is stored with the field and at the correct offset for the
>>> "new" instruction.
>>> I looked at this with multiple constructors and, also considering the
>>> logic of normalizeDefs, this should be correct - all constructors will
>>> be generated with the same prefix for field initializers and the
>>> bytecode offset is valid.
>>>
>>> However, if a constructor calls a "this" constructor, as in:
>>>
>>> class Test {
>>> Object f = new @TA Object();
>>>
>>> Test() {}
>>> Test(int x) { this(); }
>>> }
>>>
>>> This code still attaches @TA to field "f" with offset 5. However, now
>>> this offset is only valid in the first constructor, not in the second,
>>> which is:
>>>
>>> Test(int);
>>> descriptor: (I)V
>>> flags:
>>> Code:
>>> stack=1, locals=2, args_size=2
>>> 0: aload_0
>>> 1: invokespecial #4 // Method "<init>":()V
>>> 4: return
>>>
>>> Is this the intended logic, i.e. should type annotations on fields
>>> give the offset to the "initial constructors" (which is a term I found
>>> in Gen - I'm not sure whether there is a JLS term for this)?
>>> Or is this not the intended behavior?
>>>
>>> For instance initializers, all type annotations from their bodies
>>> should appear on all initial constructors.
>>>
>>> And for class initializers, all type annotations from their bodies
>>> should appear on the clinit symbol.
>>>
>>> Does all of this sound correct?
>>>
>>> Thanks,
>>> cu, WMD.
>>>
>>>
>>> On Wed, Feb 20, 2013 at 7:59 PM, Jonathan Gibbons
>>> <jonathan.gibbons at oracle.com> wrote:
>>>>
>>>> On 02/18/2013 05:23 PM, Werner Dietl wrote:
>>>>>
>>>>>
>>>>> Jon, all,
>>>>>
>>>>> I just noticed that type annotations in a static initializer block are
>>>>> not stored in bytecode.
>>>>> Take this example:
>>>>>
>>>>> class Test {
>>>>> static {
>>>>> Object o = new @TA Object();
>>>>> }
>>>>> }
>>>>>
>>>>> Attr.visitBlock correctly attributes the annotation and in the end
>>>>> localEnv.info.scope.owner contains the @TA annotation.
>>>>> The owner.toString() gives "Test" and is of kind STATIC_INIT.
>>>>>
>>>>> However, in
>>>>> com.sun.tools.javac.jvm.ClassWriter.writeMethod(MethodSymbol)
>>>>> I later see a MethodSymbol for "<clinit>()" of kind STATIC_INIT.
>>>>> This symbol no longer contains the type annotations and therefore
>>>>> nothing gets written to bytecode.
>>>>>
>>>>> Can somebody point me to the place where the MethodSymbol from Attr is
>>>>> re-written into the MethodSymbol that ends up in the ClassWriter?
>>>>> I spent some time looking through the code but didn't find this.
>>>>> Maybe type annotations should be copied over whenever one symbol is
>>>>> re-written into another one?
>>>>>
>>>>> Thanks for any hints!
>>>>> cu, WMD.
>>>>>
>>>>
>>>> In general, this sort of work is done in Lower. However, in this case,
>>>> a search on Names.clinit shows nothing there. But in Gen, you can find
>>>> this code:
>>>>
>>>> // If there are class initializers, create a <clinit> method
>>>> // that contains them as its body.
>>>> if (clinitCode.length() != 0) {
>>>> MethodSymbol clinit = new MethodSymbol(
>>>> STATIC | (c.flags() & STRICTFP),
>>>> names.clinit,
>>>> new MethodType(
>>>> List.<Type>nil(), syms.voidType,
>>>> List.<Type>nil(), syms.methodClass),
>>>> c);
>>>> c.members().enter(clinit);
>>>> List<JCStatement> clinitStats = clinitCode.toList();
>>>> JCBlock block = make.at(clinitStats.head.pos()).Block(0,
>>>> clinitStats);
>>>> block.endpos = TreeInfo.endPos(clinitStats.last());
>>>> methodDefs.append(make.MethodDef(clinit, block));
>>>> }
>>>>
>>>> -- Jon
>>>
>>>
>>>
>>>
>>
>
>
>
> --
> http://www.google.com/profiles/wdietl
--
http://www.google.com/profiles/wdietl
More information about the type-annotations-dev
mailing list