JDK-8062582 : Classfile format contains insufficent information for implementing parts of the reflection API
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 8
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-10-30
  • Updated: 2018-11-02
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.
Related Reports
Blocks :  
Blocks :  
Relates :  
There is an outstanding problem with generic type signatures and type annotations, in that javac doesn't generate either with synthetic/mandated parameters taken into account.  At least the latter (type annotations) is an outstanding javac bug.

This causes an issue that when there are synthetic/mandated parameters, getGenericParameterTypes() returns a *different sized* array depending on whether or not generic information is present.  This affects getAnnotatedParameterTypes similarly.  The array returned from getParameterTypes() /always/ contains synthetic/mandated parameters.

The behavior profile is as follows:

If generic signatures exist in the class file:

1) getGenericParameterTypes returns an array without synthetic/mandated parameters
2) getAnnotatedParameterTypes returns an array of AnnotatedTypes without synthetic/mandated parameters, the annotations are on the correct parameter.

If generic signatures do not exist in the class file:

1) getGenericParameterTypes returns the same result as getParameterTypes (which contains synthetic/mandated parameters)
2) getAnnotatedParametertypes returns an array of AnnotatedTypes with synthetic/mandated parameters represented, but the annotations will be on the wrong parameter in some cases (this is due to JDK-8029012)

A root cause of these problems is that there is no general way to tell whether a given parameter is a formal parameter, or is a synthetic or mandated parameter, and thus there is no general way to map between the formal parameters and the actual parameters.

This, in turn, makes it extremely difficult to fix issues like JDK-8055063, and makes a perfect fix impossible, because it is not generally possible to know whether a given parameter is synthetic/mandated or not.

Note: this could be fixed completely by having javac generate generic signatures that also contain information about synthetic and mandated parameters.

Additionally, some aspects of the reflection API (Parameter.getAnnotatedType()) on a receiver parameter currently cannot be implemented properly.
Update: this causes problems for javac too. When javac goes to read in types from a class file, it tries to look at generic signatures and type annotations. However, we run into the same issues wherein generic signatures can't be mapped generally to method descriptors, and therefore, type annotations.

It's not really necessary for Executable#getAnnotatedParameterTypes to return such "good" Type objects, but OK, I see how the common getAllGenericParameterTypes method lets Parameter.getParameterizedType() always return something. Few points on the body of getAllGenericParameterTypes: - The comment "// Otherwise, use the non-generic parameter data." is not accurate - you might return generic (Signature-derived) Type objects. - It would be nice in the body of Executable.java to document that i) method privateGetParameters pertains to the MethodParameters attribute, ii) method hasGenericInformation pertains to the Signature attribute, and iii) field hasRealParameterData means "A MethodParameters attribute is available". - There is no test for the changes in this bug (8062582). The new test case is for a related bug (8055063).

Changeset http://hg.openjdk.java.net/jdk9/dev/jdk/rev/23e10e5df0b3 addresses the issue as best it can, for the time being. It introduces a method, getAllGenericParameterTypes, which attempts to return an array of generic Type's that includes synthetic parameters. In some cases, it cannot do this, and will return an array of non-generic Type's. It also makes assumptions about the ordering of parameters in a method descriptor vs a generic signature, and thus uses MethodParameters data to construct an array containing generic Type's. This will work for javac, but will not work if the compiler rearranges the order of formal parameters in order to construct a method descriptor.

The patch I have for JDK-8055063 does this, and it also returns generic information where getGenericParameterTypes().length == getParameterTypes().length. If JDK-8029012 is fixed, then I think that's about the best that can be done at this time.

True, Parameter.getParameterizedType() is writing checks that the class file can't cash. In certain happy situations, Parameter.getParameterizedType() can usefully return the same Type object as [an array element from] Executable.getGenericParameterTypes(): 1. If Signature is not available. 2. If Signature is available, and indicates the same number of parameters as the method descriptor. (Notice that in both cases, getGenericParameterTypes().length == getParameterTypes().length.) In unhappy situations - where Signature is available but there's a mismatch between its explicitly declared parameters and the method descriptor's physical truth - I think Parameter.getParameterizedType() should return null for all the Parameter objects of that Executable. null indicates that Core Reflection simply doesn't have an answer.

java.lang.reflect.Parameter includes a getParameterizedType() and getAnnotatedType() method. In the case of getAnnotatedType, we can use the fact that getAnnotatedParameterTypes() follows the method descriptor. Presently, getAnnotatedParameterTypes is (incorrectly) implemented using getGenericParameterTypes. This, combined with JDK-8029012, makes it unreliable for implementing Parameter.getAnnotatedType, but these can both be fixed. In the case of getParameterizedType(), however, there is no clear way to obtain the generic type using getGenericParameterTypes(), as there may be a mismatch between the generic signature and the method descriptor. The only way to obtain a parameterized type for a given parameter is through a generic signature, and there is no general way to map the parameters defined in source to those in a classfile. For example, nothing to my knowledge prevents some compiler from doing the following: signature in source: RetTy m( Ty1 arg1, ... Tyn argn) signature in bytecode: RetTy m( mandated StaticLinkTy slink, Ty1 arg1, synthetic ArgDesc1 desc1, ... Tyn argn, synthetic ArgDescn descn, mandated CapTy1 captured1 mandated CapDesc1 cdesc1 ... mandated CapTyn capturedn mandated CapDescn cdescn synthetic ExtraContext extraCtx) This is admittedly pathological, but remember that Java isn't the only JVM language and javac isn't the only Java compiler.

The desired model is as follows: - getParameterTypes() follows the method descriptor. It contains parameter descriptors for i) explicitly declared parameters, ii) implicitly declared parameters (a.k.a. mandated), and iii) parameters which are neither explicitly nor implicitly declared (a.k.a. synthetic). There is no way to tell from a parameter descriptor which kind of parameter it represents. - getGenericParameterTypes() aims to describe the explicitly declared parameters of a method. The Java compiler is meant to create a Signature attribute for just those parameters. When there's no Signature attribute available, getGenericParameterTypes() has no choice but to describe all the parameters, i.e. follow the method descriptor. - getAnnotatedParameterTypes() aims to describe all the parameters of a method, i.e. follow the method descriptor. The RuntimeVisibleTypeAnnotations attribute, in its formal_parameter_target structure, associates type annotations with a parameter descriptor in the method descriptor. (Similarly, RuntimeVisibleParameterAnnotations associates declaration annotations with a parameter descriptor in the method descriptor.) The Signature attribute with its focus on explicitly declared parameters is not relevant. This all suggests that mapping is unnecessary. In particular, the MethodParameters attribute should not be used to try to understand how Signature relates to the method descriptor, since the availability of both MethodParameters and Signature is difficult to predict. tl;dr It's always been possible, when Signature is available, for getGenericParameterTypes() to give radically different answers than getParameterTypes(). It's not a surprise that getGenericParameterTypes() can give different answers than getAnnotatedParameterTypes() too.