User model stacking: current status

Brian Goetz brian.goetz at oracle.com
Tue Jun 14 19:18:39 UTC 2022



On 6/14/2022 2:54 PM, Dan Heidinga wrote:
>> But (and there's a whole conversation to be had here) it does mean that
>> there is separate access control on LFoo vs QFoo,
> Pulling on this thread a little, is it the class that has different
> access control or something else?

We've meandered a bit over the years on the distinction between the 
class Foo, the types Foo.ref and Foo.val, and their respective mirrors.  
It's probably time for a check-in on where we are there.

Today, Integer is a class, with a full-power mirror Integer.class; int 
is a type, with a limited mirror int.class, whose job is mostly limited 
to reflecting over field and method descriptors.

With Valhalla, Point is a class, with types Point.ref and Point.val; 
Point.class is a full-power mirror for Point.ref, and Point.val.class is 
a limited mirror that is analogous to the int mirror.  If you ask a 
Point for its getClass(), it always returns Point.class.  It would not 
bother me if the Point.val.class mirror is fully limited, and the only 
way to do lookups (e.g., getMethods) is to switch over to the class 
mirror (for which we'd probably have a `Class::getPrimaryClass` method.)

Having the two encode separate accessibilities sounds a little messy, 
but the various Lookup::checkAccess methods key off of a Class, so that 
seems a reasonable place to hang this information.  I would assume such 
checks would check both the primary class and then the secondary class, 
or we'd arrange that the primary mirror always was at least as 
accessible as the secondary mirror.  (Protected isn't a useful option, 
and public/package/private are suitably ordered.)

At the language level, there is a question about how to present the val 
class.  One obvious (but possibly bad) idea is to pretend it is a 
special kind of nested class (which is supported by the Point.val naming 
convention):

     value class Rational {
         private class val { }
     }

This is a little cheesy, but may fit mostly cleanly into user's mental 
models.   In this case, the accessibility is going on something that 
looks like a class declaration at the source level, and which has a 
class mirror at the VM level, but it really a "shadow class", just like 
the class described by int.class (or String[].class.)  This might be OK.

At one point I considered whether we could hang this on the 
accessibility of the no-arg constructor, but I quickly soured on this 
idea.  But that has an accessibility too.

Or we could invent a new kind of member, to describe the val projection, 
and hang accessibility on that.

> To create an identity object, we do access control on the both class
> (public/package) and the constructor (public/package/private (aka
> nest)).
> To create a value object, we do nest mate access control (aka private)
> on the bytecodes that create values (aconst_init / withfield).  This
> proposal extends the nest mates access check to the default values of
> Qs.

It's not just nestmate access control; it would be reasonable to declare 
the val as package-access, and trust your package mates too.

> In both cases, we're looking at the access control of two things - the
> class and the "creator of instances".  Are we applying different
> access control to LFoo vs QFoo, or to construction mechanisms?

The thing we're trying to protect is creation of uninitialized 
heap-based instances.  But it felt a little weird to draw such a complex 
line; seemed simpler (and not giving up much) to access-control the type 
name.  But we can explore this some more. Maybe we are 
access-controlling the `defaultvalue` bytecode, since its effectively 
public if someone can create a flat array.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-observers/attachments/20220614/f37978a3/attachment.htm>


More information about the valhalla-spec-observers mailing list