FYI, we just had a useful conversation about how the new readability restrictions from Jigsaw modules should affect the MHs.Lookup API. Specifically, we need a couple more access modes to represent the new layers of type readability (interacts with lookup and accessibility), plus a special behavior (another access mode bit) for publicLookup, to represent the fact that there is no longer a true global scope.
Given the increased complexity of access layers we also want to add a (trivial) API point to directly drop access mode bits from a Lookup. See http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005829.html (copied below) and nearby messages. The Jigsaw guys have a charming name for what Lookup.in does: It "teleports" a lookup to another location. Teleporting outside a module tends to shut down all access rights (to preserve monotonicity of privilege), except for publicLookup which retains its special globalized viewpoint. This is a refinement on the current Lookup enhancement in the Jigsaw repo. As one consequence, the publicLookup.lookupClass can go back to being Object, and is usefully teleportable elsewhere, so that (in turn) it is broadly useful with the new API point Lookup.findClass, which uses the lookupClass as a starting point for name resolution. — John Begin forwarded message: From: John Rose <john.r.r...@oracle.com> Subject: Re: MethodHandles.Lookup and modules Date: December 18, 2015 at 12:20:08 AM PST To: Alex Buckley <alex.buck...@oracle.com> Cc: jigsaw-...@openjdk.java.net On Dec 17, 2015, at 6:01 PM, John Rose <john.r.r...@oracle.com> wrote: > > I think I would prefer case 2. The user model is PUBLIC is the weakest > (non-empty) access > mode available to bytecode behaviors. As such it respects the LC's position > in the module > graph, and excludes module-private, package-private, and class-private. > UNCONDITIONAL > is the special thing provided by publicLookup, which ignores the module > graph. Then > PACKAGE opens up the LC's package, MODULE opens up the LC's module, and > PRIVATE > opens up the LC itself (plus its nestmates). Feels pretty good, especially > since MODULE > and PACKAGE continue to have a parallel sense of restriction. > > What do you think? So I caught you in the hall and we talked, and this seems agreeable to us both, perhaps with a name change to UNCONDITIONAL, and also a distinction between PUBLIC and QUALIFIED (as you originally proposed). To try and tease out some symmetry here: - Always, any type T is accessible to itself, when T = LC. - PACKAGE mode: Any type T is accessible (within its own package), when PACKAGE(T) = PACKAGE(LC). - MODULE mode: A public type T is accessible (within or beyond its package), when MODULE(T) = MODULE(LC). - QUALIFIED mode: A public type T is accessible beyond its module, when IS_CE(T, LC), where IS_CE(T, LC) = IS_CONDITIONALLY_EXPORTED(PACKAGE(T), MODULE(LC)) and MODULE(LC) READS MODULE(T). - PUBLIC mode: A public type T is accessible beyond its module friends when IS_UE(T, LC), where IS_UE(T, LC) = IS_UNCONDITIONALLY_EXPORTED(PACKAGE(T)) and MODULE(LC) READS MODULE(T). These conditions can be tested independently. PACKAGE implies MODULE, but everything else is disjoint. Also: - UNCONDITIONAL: In this mode, a type T is accessible if IS_UNCONDITIONALLY_EXPORTED(PACKAGE(T)), regardless of LC. - PRIVATE/PROTECTED: These protection modes apply only to non-types (JVM does not enforce "private" on classes). - NOACCESS: This is not a mode but the absence of any combination of modes; no access is allowed. The publicLookup should have UNCONDITIONAL and PUBLIC set. An original full-power lookup does *not* have UNCONDITIONAL set, just PUBLIC. The purpose of UNCONDITIONAL is to allow publicLookup to be unconcerned (as documented) about its LC. We can restore LC to be java.lang.Object. The distinction between QUALIFIED and PUBLIC is present simply because of the logical fact (as you point out) that, if you teleport to a new module, you must lose your qualified imports, but you shouldn't lose your unconditional ones. The distinction between PUBLIC and UNCONDITIONAL is present in order to capture the differing behaviors of lookups derived from publicLookup and those derived from full-power lookups. The presence of MODULE captures the larger but package-like scope of a module's internal names. About "mode stripping": You suggested (which sounds OK) that there is no need to "validate" bit masks of lookup objects. Just have excludeModes clear some bits and continue. This means there can be lookups which are able to read (say) using PACKAGE mode but not PUBLIC mode. (Today's lookups can have PUBLIC without PACKAGE but not vice versa.) Each mode bit enables a single line in the above logic, and (as you can see) the lines can be applied independently. Some implications follow. When looking up a type member T.M, the additional access checking on the member's enclosing type T (from LC) may lack PACKAGE, MODULE, QUALIFIED, and PUBLIC modes (if excludeModes stripped those bits from a full-power lookup). This means that such mode-stripped lookups can (logically) only obtain members LC.M from the single lookup class LC (since a class always has access to itself). That seems reasonable and useful. A stripped lookup with only MODULE or QUALIFIED bits will never be able to "see" any T.M, since members only match in PUBLIC, PRIVATE, PROTECTED, and PACKAGE modes. Odd, and probably not useful, but logical. There does not seem to be a way to say "give me only the T.M for which T is package-private and M is public", or "give me only the T.M for which T is module-private but M is public". Those can be composed on top, especially with the new Lookup.accessClass API point as a post-test on T. As an odd use case, a stripped lookup with only PACKAGE modes will be able to see any package-mate T of LC, and any package-private API point T.M, but it won't be able to query anything *outside* of the package of T. Unfortunately, it also won't be able to query any public member T.M, unless the PUBLIC bit is present. So I suppose stripping MODULE and QUALIFIED, leaving PUBLIC and PACKAGE, would provide useful access to T.M even if M were public. As you see, I'm trying to apply the same mode bits to both types and their members, as mechanically as possible. One place where that is tricky is with UNCONDITIONAL. That is rescued by making sure that UNCONDITIONAL will not lose PUBLIC even if teleported. About monotonicity: The rules for monotonicity of access modes will be strictly applied. All transforms that create lookups from previous ones will never produce lookups with additional mode bits set. For excludeModes this is trivially true. For Lookup.in, it is true in a complex way, as modes are shed as you "teleport" farther away from the old LC to the new LC. The design principle of monotonicity of accessible types and members (which I mentioned earlier) is stressed by the case of teleporting between modules, where (you might think) that PUBLIC in module M1 should continue as PUBLIC in modules M2. But that is not monotonic. The bytecode behaviors involving cross-module readability depend on the read edges of MODULE(LC). I think the best path is pretty simple: If you teleport between modules, you lose all bits, unless UNCONDITIONAL is set, in which case you are also allowed to keep PUBLIC (since you were already ignoring the read graph). It's more obvious that teleporting between modules should immediately drop QUALIFIED. FTR, you could check the read graph and the exports and if the two LC's had exactly the same read edges, you could justify keeping the PUBLIC bit set, and similarly for the QUALIFIED bit, if you check for equal qualified exports. But I think that's too much complexity for too little benefit. Basically, only the publicLookup is able to leap between modules. Which seems just fine to me. — John
_______________________________________________ mlvm-dev mailing list email@example.com http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev