MethodHandles.Lookup and modules
Alan.Bateman at oracle.com
Mon Dec 14 15:02:12 UTC 2015
On 11/12/2015 20:38, John Rose wrote:
>>> 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).
> Does this mean adding edges to the readability graph at M?
> If so, then that's covered by the blanket exception mentioned later.
> If you mean something else, maybe we need a point 2d.
Yes, it's adding a directed edge so that M reads other modules. Only
code in M can do this.
In graph terms then the vertices in the readability graph are the
modules. A directed edge from module M to module M2 means that M reads
M2. The graph is mutable in that edges can be added via the API at
runtime. Code in module M can add a directed edge so that M reads M2.
Code in M2 might add a read edge in the other direction. Code in a
module cannot change other modules: M cannot change M2 so that M2 reads
M or M3 or any other module. For completeness then I should say that
edges are only removed when the the vertices (modules) they connect are
> That's fine. There are two main use cases for Lookup.in,
> neither of which require the tracking of long chains of L.in(A/B/C…).
> A. Agent with full-power lookup wants to invoke another agent with the
> but wants to limit access, because he doesn't fully trust the other agent.
> He does a single L.in(A) to a remote-enough type A, creating a
> non-full-power lookup.
> (Note: Picking A is sometimes non-trivial. This might be an API flaw.)
> B. Agent with full-power lookup wants to get access to private
> nestmate in A.
> He does a single L.in(A) where LC and A are in the same package member.
> This works around differences between access checks at JVM and JLS levels,
> just as the package-private accessor methods from javac do. (Yuck!)
Case A is where our current approach might be too limited. This may be
tied into the discussion point as to how to choose A. If A is in the
same module as LC then it's as before. However if code in named module M
creates a full-power lookup and chooses A in another module M2 then the
resulting L.in(A) has zero access.
It wouldn't be hard to change this to allow PUBLIC be preserved so that
L.in(A) would at least allow access to public types in packages that are
exported unconditionally by the modules that m(A) reads. Would that
increase usefulness? Would such cases be cases where PL is equally useful?
Preserving PUBLIC would mean compromising on the guarantee that there be
no more access that the original but that is only because m(A) might
read modules that m(LC) does not read. It would not give access to
non-exported types in m(A). It would also not give access to public type
in packages that are conditionally exported to m(A).
> Going back to graceful degradation:
> 3e. If A is in a named module and LC in the unnamed module (or L is a
> public lookup), only public names readable from A are retained
> And then we get:
> 4. downward convergence to empty privileges
> 4a. if A is inaccessible to LC=L.lookupClass, L.in(A) has no privileges
> 4b. if A and LC are in different named modules, L.in(A) has no
> privileges (there is no attempt to retain an intersection of
> readability sets)
Yes to both.
>> 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.
> Right. (FWIW, the term "unconditional" is not in the Lookup javadoc.)
I looked over the javadoc after Stanislav's first mail and it does need
to be expanded and improved. In particular, It does not make clear that
it does not allow access to types in packages that are exported
conditionally (or "qualified exports" as we have been calling them).
> Should there be a way to build a lookup, for two modules M1/M2, which
> reads those names of M2 which M1 can read, except no internals
> of M1? I wonder if such a thing would be useful? Probably not.
> But it would be useful to have a lookup in a module M1 which can
> read the exports of *every* M2 that M1 can see, except no M1 internals.
> (This includes the unconditionally exported public names of M1.)
> This would be a Lookup with an LC in M1 and flags of PUBLIC only.
> I guess that is the effect of PL.in(M1) and point 3a, right?
Yes, PL.in(M1) gives us this.
More specifically, if C1 is a public and in a package exported by M1
then PL.in(C1) will result in a Lookup that is C1/public. This lookup
can only be used to access public types in packages that are exported
unconditionally and only by modules that M1 reads (assume M1 reads itself).
Alternatively, if C2 is a type in M2 that is not in an exported package
then PL.in(C2) will result in a Lookup that is C2/noaccess. This is of
course because PL.lookupClass() has no access to C2.
> OK, then:
> 5. publicLookup has a reasonable minimal set of globally acceptable
> 5a. this set of privileges is singular, and does not depend on the
> lookupClass (but it will always be in the unnamed module)
> 5b. the only possible results of publicLookup.in(A) are no change, and
> (following 3e) a public-only access from a named module
Yes to all of these.
>> We have looked of taking snapshots and persisting intersections but
>> it diverges from bytecode behavior which I think rules it out.
> Well, other lookups diverge from bytecode behavior also, but only by
> dropping away access modes (like private, package, etc.).
> This covers use case A above. You could argue that intersecting
> readability sets is useful in a similar way, but it is way too complex.
> We don't aspire to create a Lookup object on exactly three API
> points and no more—that's overkill. A hypothetical application
> that wants to express intersections (or unions) of Lookup capabilities
> can build this on top of lookups.
Yes, the intersection would be complex, more so when qualified exports
are taken into account. Preserving PUBLIC might be a compromise,
assuming it is useful.
More information about the jigsaw-dev