JEP 401 -- reflection and class literals

Remi Forax forax at univ-mlv.fr
Tue Jun 29 17:22:47 UTC 2021


> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "valhalla-spec-experts" <valhalla-spec-experts at openjdk.java.net>
> Sent: Mardi 29 Juin 2021 18:48:25
> Subject: Re: JEP 401 -- reflection and class literals

> The general consensus here is that this stacking is slightly better than the
> previous one, so let's take this as the plan of record. Now, to explore the
> next turn of the crank...

> The concerns that were raised could be characterized by "do we *really* need to
> even have separate class literals for Foo.ref and Foo.val?" And the answer is,
> sort of yes, sort of no, so there is a possible next turn of the crank that
> might or might not be a further improvement.

> Two existing constraints we have are:

> - Reflection (and MethodHandle) already do everything with jl.Class. That means,
> for example, we had to invent the primitive mirrors a long time ago, so that
> you could describe a field or method parameter of type int. "Fixing" this is
> not really on the table right now.
> - The language has given this some surface; you can say `int.class` (which maps
> to`getstatic Integer.TYPE`.)

> Given all this, reflection needs to be able to differentiate between LFoo and
> QFoo, which suggests a second mirror. And the user needs to be able to express
> this mirror to call getMethod() or Lookup::findXxx (and to interpret the
> results of reflective lookups). But maybe we don't need to give it so much air
> *in the language*, especially given how confusing it is because the language
> and VM views (which reflection is closer to) don't align entirely.

> Rather than talking about the ref vs val mirror (which is confusing, because
> getClass returns the same thing whether something is stored in a .ref or a
> .val), perhaps we can reframe in terms of the "class mirror and the flattened
> representation mirror", or the "primary mirror and the restricted mirror", or
> some other such (though, even the term "mirror" is foreign to users, since all
> the see is jl.Class.)

> In this model, Point.class would be the only mirror expressible directly in the
> language, and it would always be the primary mirror. Reflection users would
> need to navigate to the secondary mirror through something like
> `Point.class.secondaryMirror()` or some other method on jl.Class. By not
> putting this in the language, we avoid some of the confusing aspects of this
> story.
+1 

Rémi 

> On 6/23/2021 11:13 AM, Brian Goetz wrote:

>> In working through the details of reflective support in JEP 401, I think we've
>> fallen into a slight "false consistency" regarding class literals. (This is a
>> complicated network with many interrelated moving parts, so I'm not looking for
>> "quick answers" here, as much as balancing global consistency with an
>> understandable user story.)

>> In the language, a primitive class declaration gives rise to a class (P) and
>> three types: P, P.ref and P.val. P is an alias for one of them; most of the
>> time, it's an alias for P.val, but for migrated value-based class, it's an
>> alias for P.ref. The language has a concept for tuning this mapping, currently
>> under the name `ref-default`. (The VM would like to remain ignorant of details
>> like this.)

>> In the VM, there is one class (P), whose instances can have two representations,
>> flattened and indirect, described by two descriptors (QP and LP).

>> The VM and reflection need mirrors for both of these descriptor types. This is a
>> problem we face today to some degree for the primitive types; there's a
>> "neutered" mirror for int.class that doesn't correspond to an actual declared
>> class with limited operational capability. These "secondary" mirrors are only
>> used for reflection / method handles, to reflect method and field descriptors.

>> We double down on this story for mirrors for primitive classes. Each primitive
>> class has a primary (unrestricted) mirror corresponding to the L descriptor,
>> and a secondary (restricted, flattened, possibly null-free) mirror
>> corresponding to the Q descriptor. The secondary mirror has only one job:
>> reflecting Q descriptors in method and field descriptors (and supporting method
>> handles in their emulation of same.)

>> When you ask an object for getClass(), it always hands back the primary mirror.

>> As a bonus, this has a nice compatibility story with Integer today; reflective
>> code is used to testing for Integer.class when an int is expected (reflection
>> is always boxed), so this will continue to work, because instances of `int`
>> will report `Integer.class` from `getClass()`.

>> All of this feels like the low-energy-state for extending mirrors to primitives.
>> Here's where I think we bobbled a little bit, and can correct.

>> Currently, we say "Well, if Point is an alias for Point.val, then Point.class
>> must be Point.val.class". This is consistent, but kind of useless. Because
>> then, for example:

>> void puzzler() {
>> assertEquals(Point.class, new Point(0,0).getClass());
>> }

>> will fail (unless Point is ref-default, at which point it succeeds.) I think the
>> mistake we made is too literally following the model of "Point is an alias for
>> Point.val". So, I'm proposing a slight adjustment, that treats the unqualified
>> class name contextually:

>> - In the context of a variable type, Point is an alias for Point.val, or
>> Point.ref, if ref-default (no change from current);
>> - Where a class is needed (e.g., new Point(), instanceof Point), Point refers to
>> the class Point, not any of its derived types, and Point.{ref,val} are not
>> valid in these contexts (no change from current);
>> - Where a reflective literal is needed, the unqualified Point.class is always
>> the primary mirror, which always matches the behavior of Object::getClass
>> (change);
>> - If an explicit literal is needed for reflection (e.g., calling getMethod()),
>> you can be explicit and say Point.{ref,val}.class to say what you mean (no
>> change).

>> This aligns the meaning of "Point.class" with the actual behavior of other
>> reflective API points.


More information about the valhalla-spec-observers mailing list