Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8226916

Lookup.in should allow teleporting from a lookup class in a named module without dropping all access

    XMLWordPrintable

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P3
    • Resolution: Approved
    • Fix Version/s: 14
    • Component/s: core-libs
    • Labels:
      None
    • Subcomponent:
    • Compatibility Kind:
      behavioral
    • Compatibility Risk:
      medium
    • Compatibility Risk Description:
      Hide
      Details are documented in the compatibility section in the CSR.

      The compatibility risk is marked as medium while we anticipate that
      the existing library and framework are minimally impacted by this in practice.
      Show
      Details are documented in the compatibility section in the CSR. The compatibility risk is marked as medium while we anticipate that the existing library and framework are minimally impacted by this in practice.
    • Interface Kind:
      Java API
    • Scope:
      SE

      Description

      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:

      1. three new sections added in Lookup class spec
        • Cross-module lookup
        • Cross-module access check
        • Access modes
      2. MethodHandles::privateLookupIn
      3. Lookup::in
      4. Lookup::accessClass
      5. Lookup::dropLookupMode
      6. Lookup::previousLookupClass
      7. Lookup::toString
      8. Lookup::MODULE
      9. Lookup::UNCONDITIONAL

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              mchung Mandy Chung
              Reporter:
              alanb Alan Bateman
              Reviewed By:
              Alan Bateman, John Rose
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: