Type annotations in static initializers

Werner Dietl wdietl at gmail.com
Wed Feb 20 23:57:53 PST 2013


Thanks for this pointer, Jon.

Method javac.jvm.Gen.normalizeDefs indeed seems relevant, as it
contains initCode and clinitCode for instance and class initializers -
for both of which I want to store the type annotations.
However, I cannot find the MethodSymbol that I saw at Attr.
[cl]initCode is just a List of JCStatement and I couldn't find how to
get to the MethodSymbol that I previously saw in Attr.

Any more hints? Something very obvious I'm missing?


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


More information about the type-annotations-dev mailing list