enhanced-metadata-spec-discuss Digest, Vol 12, Issue 4

Alex Buckley alex.buckley at oracle.com
Tue Sep 3 17:01:50 PDT 2013


Hi Jesper,

On 9/3/2013 3:38 PM, Jesper Steen Møller wrote:
>>> // --
>>> public class X {
>>>      void foo() {
>>>          new Y().new Z() {
>>>          };
>>>      }
>>> }
>>> class Y {
>>>      class Z {}
>>> }
>>> // --
>>>
>>>    final X this$0;
>>>      descriptor: LX;
>>>      flags: ACC_FINAL, ACC_SYNTHETIC
>>>
>>>    X$1(X, Y);
>>>      descriptor: (LX;LY;)V
>>>      flags:
>>>      Code:
>>>        stack=3, locals=3, args_size=3
>>>           0: aload_0
>>>           1: aload_1
>>>           2: putfield      #1                  // Field this$0:LX;
>>>           5: aload_0
>>>           6: aload_2
>>>           7: dup
>>>           8: invokevirtual #2                  // Method
>>> java/lang/Object.getClas
>>> s:()Ljava/lang/Class;
>>>          11: pop
>>>          12: invokespecial #3                  // Method
>>> Y$Z."<init>":(LY;)V
>>>          15: return
>>>        LineNumberTable:
>>>          line 3: 0
>>>      MethodParameters:
>>>        Name                           Flags
>>>        this$0                         final mandated
>>>        x0
>>>
>>> Why would the anonymous class's own enclosing instances be mandated
>>> while "x0" which I presume is the super classes enclosing instance
>>> be flag less ?
>>
>> There seem to be two javac (or maybe javap) bugs here:
>>
>> - The this$0 field and the this$0 formal parameter which supplies
>> it are concerned with the immediately enclosing instance of the
>> anonymous class's instance. Transmitting this instance to the
>> anonymous class's instance is covered by point 2 in 8.8.9.
>> Basically, it's not specified. As such, the this$0 field has been
>> ACC_SYNTHETIC since forever, and that's a big hint that the this$0
>> formal parameter should be ACC_SYNTHETIC too.
>>
>> - The x0 formal parameter is implicitly declared by 15.9.5.1 as
>> having type Y, because the direct superclass Z of the anonymous
>> class is inner. So, x0 should be flagged and displayed as
>> mandated.
>>
>> Alex
>
> According to 8.8.9 (3) and 15.9.5.1(second bullet), x0 would have to
> an INITIAL / "the first" implicitly declared argument, so shouldn't
> javac also change the order of this$0 and x0 (even if the sole
> purpose of doing so is to provide it to Y$Z(Y) constructor).

The ctor of X$1 is specified to have an initial implicitly declared
parameter to represent the immediately enclosing instance of the
anon.class object with respect to Z, i.e. the "new Y()" instance. But a
Java compiler is free to add synthetic parameters to the ctor of an
anonymous class, because (per 8.8.9 (2)) no other compiler will ever
compile code which references the anonymous class. So, the ctor ends up
having the descriptor X$1(X, Y); - the first parameter descriptor is the
synthetic this$0 of type X, and the second parameter descriptor is the
mandated x0 of type Y.

> But the thing I don't understand is why the spec bothers with
> specifying implicitly declared parameters for constructors for
> constructors of anonymous constructors when they generally fall into
> the category of 8.8.9 (2). Why can't the compiler simply take the
> synthetic x0 argument and use it as (the mandated) constructor for Z,
> which may indeed be supplied by a different compiler. Doing that
> shouldn't make x0 mandated, since no other compiler may emit a call
> to the X$1(X, Y) constructor.

What's the point of an anon.class's constructor ever having a mandated 
parameter, whether the anon.class's superclass is inner or not? The 
answer is: there is no point, but the JLS was written to require the 
parameter anyway. 15.9.5.1 has specified since JLS2 that an anon.class's 
constructor has an implicit first parameter (yeah yeah it ends up being 
the second), while 15.9.3 has specified since JLS2 the argument to be 
passed to that parameter. Given these rules, I think "the right thing" 
is to flag the parameter as mandated in the MethodParameters attribute.

One could argue that an anon.class's constructor should be synthetic
because of the rule in 13.1 about not corresponding to something in
source code; and then argue that all of its parameters should be
synthetic, with none mandated. But we've evolved our understanding of
class file contents since the 13.1 rule was written in the 1990s.
Notably, JLS3 specified that an enum type has values() and valueOf()
methods - you can't write them by hand, so they must be emitted into the
class file without corresponding to anything in source code, yet they
cannot be synthetic because they ought to show up in core reflection
(synthetic methods don't show up). We have a whole family of bugs about
this, which led to a better understanding of synthetic v. mandated.
Eventually I would like to flag an anon.class's constructor as mandated
rather than synthetic, while will require an ACC_MANDATED flag for
classes, fields, or methods.

> In short, I don't see any practical need for 8.8.9 (3) -- did I miss
> something crucial?

It would have been better if JLS2 had said how to determine the
immediately enclosing instance with respect to the superclass, and then
- rather than calling for "automatically generated" arguments and
parameters - not specified how to transmit the instance to the new
anon.class object. The JLS could have decreed that a newly constructed
anon.class object simply knows of the instance. But it's too late to
change that now, since compilers emit the anon.class constructor
specified in 15.9.5.1. For the sake of full information, the policy in 
8.8.9 (3) should remain.

Alex


More information about the enhanced-metadata-spec-discuss mailing list