JDK-8284333 : Constructor.getAnnotatedParameterTypes returns wrong value for Enums
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 17,18,19
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2022-04-01
  • Updated: 2023-09-25
  • Resolved: 2023-09-25
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
The return result of Constructor.getAnnotatedParameterTypes() for enum constructors are wrong, that it maps annotations on formal parameters to descriptor-based parameters without an offset of 2. The annotation attribute in the class file seems correct, and this issue happens either when -parameter flag of javac is set or not.
The non-type-annotation equivalent for enum was fixed in JDK-8263763, and the type annotation inner class equivalent was fixed in JDK-8074977.

May be feasible to unify the offset detection mechanism for both type annotations in TypeAnnotationParser and regular annotations in Executable.handleParameterNumberMismatch, or even that for signatures for formal parameters. Currently there are 3 different systems for mapping them, which is costly for maintenance and is a blind spot in Java Language and JVM specifications.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Write an enum constructor that use TYPE annotations on its parameters. Observe that the type annotations

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The annotated type of the cmp argument should be @TypeAnno Comparator when inspected by reflection; when javac compiles this class with -parameters flag, it should be presented as @Comparator<@TypeAnno String>. The annotated type of the synthetic $enum$name argument (param 0) should be String.
ACTUAL -
The annotated type of cmp is Comparator or Comparator<String> (with javac -parameters flag); the annotated type of $enum$name is @TypeAnno String.

---------- BEGIN SOURCE ----------
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

enum EnumCtor {
	;
	EnumCtor(@TypeAnno Comparator<@TypeAnno String> cmp) {} // relevant content

	public static void main(String... args) throws Throwable {
        inspect(EnumCtor.class.getDeclaredConstructor(String.class, Integer.TYPE, Comparator.class));
    }

    static void inspect(Executable ctor) {
        var params = ctor.getParameters();
        System.out.println("Params:\n" + Arrays.toString(params));
        System.out.println("Param types:\n" + Arrays.toString(ctor.getParameterTypes()));
        System.out.println("Generic Params:\n" + Arrays.toString(ctor.getGenericParameterTypes()));
        System.out.println("Annotated Params:\n" + Arrays.toString(ctor.getAnnotatedParameterTypes()));
        System.out.println("Receiver:\n" + ctor.getAnnotatedReceiverType());
        for (int i = 0; i < params.length; i++) {
        	var p = params[i];
            System.out.println("Param " + i + ":\n" + p.getType() + ", " + p.getParameterizedType() + ", " + p.getAnnotatedType());
        }
        System.out.println();
    }

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface TypeAnno {}

---------- END SOURCE ----------

FREQUENCY : always



Comments
This is no longer replicable on JDK 21, most likely fixed by JDK-8292275.
25-09-2023

The observations with Windows 10: JDK 17: Failed with the following outputs Params: [ java.lang.String $enum$name, int $enum$ordinal, java.util.Comparator<java.lang.String> cmp] Param types: [class java.lang.String, int, interface java.util.Comparator] Generic Params: [java.util.Comparator<java.lang.String>] Annotated Params: [@TypeAnno() java.lang.String, int, java.util.Comparator<java.lang.String>] Receiver: null Param 0: class java.lang.String, class java.lang.String, @TypeAnno() java.lang.String Param 1: int, int, int Param 2: interface java.util.Comparator, java.util.Comparator<java.lang.String>, java.util.Comparator<java.lang.String> JDK 18: Failed. JDK 19e+3: Failed.
05-04-2022