John,

Thanks for jumping in on this and the guidance. Comments inline.


On 10/12/2015 23:08, John Rose wrote:

This stuff makes my head hurt, but I'm fine with any semantics that preserves the following:

1. full power: MethodHandles.lookup() has the same privileges as (non-constructor) bytecode in the caller class
Preserved, no changes.


2. downward monotonic
2a. L.in(A) never has more privileges than L (for all L, A)
2b. L.in(A) never has more privileges than a full power lookup on A (for all L, A) 2c. as a corollary, a chain L.in(A).in(B).in(C)… has no more privileges than L or any lookups in A, B, C, …
Preserved with the exception of A, B and C in the same named module M and where the set modules that M reads increases (say where code in M reads additional modules).


3. graceful degradation: L.in(A) loses only privileges broken by the distance between LC=L.lookupClass and A
3a. if A==LC no privileges are lost; the identical L can be the result
3b. if A and LC are nestmates, only protected privileges may be lost (dynamic emulation of JLS nestmate access) 3c. if A and LC are in the same package, only private privileges may be lost 3d. if A and LC are in the same module, only package privileges may be lost
Preserved but perhaps with the (initially surprising) consequence that all access is lost when m(LC) is a named module and m(A) is a different module. This arises because the two modules may read very different sets of modules, the intersection cannot be expressed via a lookup class + modes.


4. downward convergence (to publicLookup or empty privileges)
4a. if A is inaccessible to LC=L.lookupClass, L.in(A) has no privileges (less than publicLookup) 4b. if A is accessible to LC and L has non-empty privileges, L.in(A) is no less privileged than publicLookup 4c. for any L with non-empty privileges, there is a sequence of types A,B where L.in(A).in(B) is equivalent to publicLookup
Downward convergence to zero access/empty privileges.

No downward convergence to publicLookup because m(publicLookup.lookupClass) must read all modules, thus a superset of the modules that named modules will read. I should say of course that the publicLookup can only be used to create method handles to public members in packages that are exported unconditionally. So nothing that code in a named module couldn't otherwise access when it increases readability.



5. publicLookup has a reasonable minimal set of globally acceptable privileges 5a. this set of privileges is singular, and does not depend on the lookupClass 5b. the only possible results of publicLookup.in(A) are no change, and falling to empty privileges
publicLookup is minimally trusted and so can only create method handles where the target type is public and exported (unconditionally).

For 5b then publicLookup.in(A) may result in no change or degrade but not to empty privileges in one hop. The "no change" case is where A is in an unnamed module (think class path). The "degrade" case is where A is a named module and so the resulting lookup can only be used to access the public types that are exported unconditionally by modules that m(A) reads.



Access can only be measured against the current state of the module graph,
which means that certain results can vary over time, in a consistent way
across the whole system.
Yes, consistent with bytecode and also the access checks in core reflection.


The above are my preferences; I can imaging tweaking some of those things
in order to make the API fit more gracefully into modules. I rely on Alan & co.
to figure that out!
and we rely on you to ensure that what we do make sense. I hope this does not constitute a circular dependency :-)


:


(BTW, see https://bugs.openjdk.java.net/browse/JDK-8145070 )
The defining loader of publicLookup.lookupClass() is no longer null and looks like Nashorn trips up on the permission check in Class::getClassLoader.





I agree with this. Lookups are primarily about emulating bytecode behavior (not reflection behavior), and providing a framework for safe and sane delegation.
Adding edges to the readability graph impacts all access checks consistently and I think in a safe way.



:

Does publicLookup.in(A) create a new set of privileges depending on A?

I think that is the current design; maybe it's best, but it's a little odd.
If there really is a uniquely minimal set of public privileges,
then that should be publicLookup, and PL.in(A) should not affect
that minimal set.  That's the intention of saying the LC of PL is
mere convention.
As publicLookup is no longer the minimal set of privileges then publicLookup.in(A) will impact reduce access for the case that m(A) is a named module. Stanislav makes a good point about the javadoc as a named module may not read all modules and so publicLookup needs to be in an unnamed module for now.



We can modify axiom #5 to say:  PL accesses a unique family of
privileges sets, indexed by the LC of each PL.  This family is
minimal in the sense that, for any PL in the family, and any
lesser privileged L, L is either another member of the PL family,
or has empty privileges.

In this case, the LC of a PL provides a scoped or restricted
view of "really public" names, all of which are non-problematic
to view from any point in the system.  The fact that you have
to dial in a special LC to get to one of those names is merely
a formality.  No abstraction can ever be broken via a PL,
even if the various PLs have slightly different capabilities.

(N.B.  Because of 4a, PL.in(A) can go empty, if PL.LC cannot
access A.)

I hope the adjustments that we have are reasonable. We start with a singleton PL that can produce method handles to public members of exported packages. It can then be restricted, with PL.in(A), to just the public types exported unconditionally by A or the modules that A reads.

We have looked of taking snapshots and persisting intersections but it diverges from bytecode behavior which I think rules it out.

-Alan

Reply via email to