-
CSR
-
Resolution: Approved
-
P3
-
None
-
behavioral
-
medium
-
-
Java API
-
SE
Summary
This CSR proposes to enhance Lookup::in
and MethodHandles::privateLookup
API
for cross module teleporting and the access check to use the previous lookup class
from which this Lookup
object was teleported in addition to this Lookup
context
(current lookup class and allowed modes) such that a target class is accessible
if and only if it is equally accessible to both the previous lookup class and current
lookup class.
Problem
Lookup::in
API currently does not support cross module teleporting.
It currently specifies to drop all access when teleporting from
a lookup class in a named module to a lookup in another module.
This proposes to enhance Lookup::in
and MethodHandles::privateLookupIn
API to perform double module access when the Lookup
object
is a result of cross module teleporting.
Solution
Extend Lookup
object to maintain a previous lookup class when
a Lookup
object is created from cross-module teleporting.
Lookup::in
and MethodHandles::privateLookupIn
produces
a Lookup
object with a previous lookup class if and only if
it teleports from a lookup on C
in one module to D
in another module.
For a Lookup
object with a previous lookup class, its lookup mode
must have MODULE bit cleared.
A new Lookup::previousLookupClass
API will be added to query
the previous lookup class, or null if not present.
Method handle lookup will perform access check against both the
lookup class and the previous lookup class, if present, and
the referenced class must be equally accessible to the module of
the previous lookup class and the lookup class as specified in
Lookup::accessClass
.
There is no change to MethodHandles::lookup
API and it produces a Lookup
object with PRIVATE, PROTECTED, PACKAGE, MODULE, PUBLIC bits set.
Incompatibility
This section summarizes the spec changes and the incompatibility for each of them.
Lookup::in
will drop all access if it hops to a third module (named or unnamed)
When Lookup::in
is invoked to teleport from lookup class C
in module M0
to D
in module M1
, the returned Lookup
will have PUBLIC bit set that can be used to access public members of
public types in any module that both M0 and M1 read the type is
in a package that is exported at least to both M0 and M1.
Lookup CL = MethodHandles.lookup(); // caller is C
:
Lookup DL = CL.in(D.class);
:
Lookup EL = DL.in(E.class);
If it attempts to teleport from C
in M0
to D
in M1
and then to
E
in M2
and they all are unnamed modules, as the example code above,
DL
is the lookup object on D
with C
as previous lookup class with
PUBLIC bit set. When this lookup object is called to teleport to E
,
the returned Lookup
object, EL
, will have all access dropped since
DL
does not have MODULE
bit. In other words, a Lookup can do
one single hop at most to produce a lookup object with PUBLIC access.
A Lookup
attempting to hop to a third module (named or unnamed)
will drop all access.
In JDK 13, the returned Lookup object on E still has PUBLIC bit set.
Existing code using a Lookup
produced by teleporting to a third unnamed
module will fail in looking up public members of public types in unnamed module.
The compatibility risk is expected to be low as we anticipate the common
usage of Lookup::in
would be intra-module.
MethodHandles::publicLookup
produces a Lookup
object with
UNCONDITIONAL
bit set
The new public Lookup object no longer has PUBLIC
bit set. No impact to what the types this public lookup can access.
publicLookup.in(C.class)
produces a new Lookup
object on C
with UNCONDITIONAL
bit set that can access any public type
in any exported API packages in any module. Now there can be
more than one Lookup
object with UNCONDITIONAL
bit set.
Previously, the Lookup
object returned by publicLookup.in(C.class)
with UNCONDITIONAL
bit dropped that can access a public type in
a package that is exported by C's module or exported by other module
that C's module reads.
The compatibility risk for the above is minimal since existing code can
only use MethodHandles::publicLookup
for public lookup.
publicLookup::dropLookupMode(UNCONDITIONAL)
drops UNCONDITIONAL
and hence the returned Lookup
object has no access.
Previously the returned Lookup
object has PUBLIC
bit remained
that can access all public members of public types in a package
that is exported unconditionally by other module that the module of
its lookup class can read.
dropLookupMode
was introduced in Java SE 9 for a library to hand
its Lookup
with limited access to a framework to access its internals.
So it is expected that dropLookupMode(UNCONDITIONAL)
on
a public lookup is very rare.
privateLookupIn
method requires both PRIVATE
and MODULE
bit
privateLookupIn
method requires both PRIVATE
and MODULE
bit
to produce a Lookup
object with private access in addition to the
existing checks for deep reflection.
Previously it requires only MODULE
access. Existing code that only
has a Lookup
object with no private access used to successfully call
privateLookupIn
method will fail with this change.
It is expected that a framework library will start teleporting with its own
Lookup
object with private access via MethodHandles::lookup
method
and this change does not impact one-hop cross module teleporting.
This impacts the second hop from a Lookup
object produced
by privateLookupIn
.
The Lookup
object produced by privateLookupIn(D,CL)
will have
C
as the previous lookup class where C
is CL's lookup class.
Lookup DL = privateLookupIn(D, CL);
:
Lookup EL = privateLookupIn(E, DL);
Previously, privateLookupIn(E, DL)
may succeed if D reads E and
E's module opens E's package at least to D's module.
With this change, DL
does not have MODULE
bit and privateLookupIn(E, DL)
will fail to get a private Lookup. Such code would need to be updated
to use a Lookup with private access.
The compatibility risk is medium although we anticipate existing library or framework
are likely using Lookup
object with private access rather than a reduced
access in practice and also rarely do multi-hop privateLookupIn
.
UNCONDITIONAL
is no longer used in conjunction with PUBLIC
No impact to existing code.
MODULE
can be dropped while other bits are set
Previously when MODULE bit is set in conjunction with PUBLIC bit, a Lookup with module mode can access all public types in the module of the lookup class and public types in packages exported by other modules to the module of the lookup class.
A Lookup with MODULE
bit set, there is no previous lookup class.
It can access all public types in the module of the lookup class.
If a Lookup on C
in M1
with a previous lookup class (PLC
in M0
)
with PUBLIC bit, it can access public members of public types in
module M2 that both M0 and M1 read the type is
in a package that is exported at least to both M0 and M1.
No impact to existing code.
Specification
Attached the javadoc of MethodHandles
and MethodHandles.Lookup
and
MethodHandles.java.patch and specdiff.
List of API change:
- three new sections added in
Lookup
class spec- Cross-module lookup
- Cross-module access check
- Access modes
MethodHandles::privateLookupIn
Lookup::in
Lookup::accessClass
Lookup::dropLookupMode
Lookup::previousLookupClass
Lookup::toString
Lookup::MODULE
Lookup::UNCONDITIONAL
- csr of
-
JDK-8173978 Lookup.in should allow teleporting from a lookup class in a named module without dropping all access
-
- Resolved
-
- relates to
-
JDK-8236945 typo "the the" in Lookup::in javadoc
-
- Resolved
-