JDK-8130087 : Allow MethodType to encode its components with Strings, not Classes
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2015-06-29
  • Updated: 2017-08-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_minorUnresolved
Related Reports
Blocks :  
Relates :  
Description
A class mentioned in field or method descriptor is not checked for accessibility by the VM.  Nor must the named class be visible to the mentioning class's class loader [1], or even to any class loader at all [2].

However, the design of java.lang.invoke requires all MethodTypes to be encoded with Class objects, which necessitates loading appropriate classes; the resolution of CONSTANT_MethodType structures in class files (JVMS 5.4.3.5) further requires an accessibility check.

This means that certain perfectly valid invocations, declarations, etc., cannot be successfully described abstractly using MethodTypes and MethodHandles.  E.g., were a class file to be preprocessed, replacing all its invokestatic instructions with "equivalent" invokedynamic instructions, new errors could occur, as the VM attempted to resolve classes that, in the original bytecode, would be left unresolved.

To address this limitation, I suggest exploring the possibility of enhancing MethodType to represent its component types as Strings as an alternative to Classes.  That may be a rather deep change, but as is the feature can't live up to its promise of equivalence with concrete bytecode.  (Expressiveness aside, it's also a potential performance problem to require every mentioned class to be loaded immediately.)

As a followup, resolution of CONSTANT_MethodType (JVMS 5.4.3.5) could then be modified to avoid loading any classes.

-----

[1] Class loader constraints ensure that two class loaders agree on the meaning of a name in a declaration/use or overrider/overridee scenario -- but *only if* both class loaders attempt to load a class with that name.  If one of them never loads such a class, the constraint cannot be violated.

----

[2] Example of a class name that goes completely unresolved:

Source file: ResolutionTest.java:

-

public class ResolutionTest {
    public static void main(String... args) {
        System.out.println(C.f);
        System.out.println(C.m());
    }
}
class C {
    static Missing f;
    static Missing m() { return null; }
}
class Missing {}

-

javac ResolutionTest.java
rm Missing.class
java ResolutionTest

Output:
null
null

(In practice, this kind of thing might arise if the classes for an optional feature of some system are sometimes unavailable.)
Comments
The thing that makes this hard is JVMS 5.3.4. Loading Constraints: "Ensuring type safe linkage in the presence of class loaders requires special care. It is possible that when two different class loaders initiate loading of a class or interface denoted by N, the name N may denote a different class or interface in each loader..." An unresolved MethodType would be (presumably) a tuple of strings and a single resolving class loader (or referencing class or Lookup). It is impossible to compare two such MethodType's for equality without loading the classes. The best we can do before loading the classes is to install class loader constraints saying that S-in-L1 must be the same as S-in-L2 for some string S in loaders L1, L2. But such constraints must be established sparingly, only when actual access or override conditions occur. A MT by itself does not have enough context to determine when and where such conditions occur, so it seems impossible to use CL constraints with MTs. Thus, comparisons between unresolved MT's must be provisional; they cannot be answered except in a specific context, or else unless the classes can be loaded at the time of comparison. It is therefore unclear how to implement the equals/hashCode API for unresolved MT's. Maybe there is a way to "detune" the API, by saying that two unresolved MT's are unequal unless they have exactly equal components (including the hidden CL component). Or (going the other way) we could use only string equality (neglecting CLs), and allow two U-MTs which are equal become unequal after resolution. Probably the former is more workable than the latter. I suppose that if we were to allow Lookup.findStatic (et al) to accept U-MTs, if the lookup succeeds but the components are not all loaded, then we could endow the resulting MH with a MH::type component which is partially (or wholly?) unresolved. But the matching of types for MH.invokeExact, etc., becomes the problem: We can no longer just do a pointer comparison on a canonicalized MT reference to ensure type correctness. Instead we have to compare the caller's U-MT against the callee's U-MT, using all available CL constraints. This is perhaps possible but much more expensive than comparison of fully resolved MTs.
22-08-2017

The VM *does* resolve classes mentioned in descriptors if necessary for verification (for example, to determine whether a Foo can be assigned to a Bar). But this is not necessary for every type mentioned in every descriptor. It might be possible to argue that the immediate resolution of types mentioned in MethodTypes is consistent with an "eager resolution" strategy for linking (JVMS 5.4); my understanding of those constraints on linking is fuzzy. It's not clear to me precisely what this means: "Errors detected during linkage are thrown at a point in the program where some action is taken by the program that might, directly or indirectly, require linkage to the class or interface involved in the error."
29-06-2015