JDK-8292275 : javac does not emit SYNTHETIC and MANDATED flags for parameters by default
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 18
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2022-08-08
  • Updated: 2024-04-29
  • Resolved: 2023-04-30
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.
JDK 21
21 b21Fixed
Related Reports
Blocks :  
CSR :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Sub Tasks
JDK-8308450 :  
Description
A DESCRIPTION OF THE PROBLEM :
Chapter 13.1 in the JLS mentions:
11. A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared explicitly or implicitly in source code, unless the emitted construct is a class initialization method 
and
12. A construct emitted by a Java compiler must be marked as mandated if it corresponds to a formal parameter declared implicitly in source code

For parameters, this information is stored in the MethodParameters attribute. However, this attribute is only emitted when compiling with the -parameters flag or if the method is a canonical record constructor.

This behavior can be observed by either looking at the produced class files using javap, or by using the Parameter#isImplicit()/isSynthetic() methods.

Additional context: https://mail.openjdk.org/pipermail/compiler-dev/2022-May/019783.html and https://mail.openjdk.org/pipermail/compiler-dev/2022-June/019924.html

I'm able to provide a fix for this.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the code below with javac without -parameters flag.
Run the code with java A.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
----------
Executable: A$Inner
Parameter at position 0
parameter.getType() = class A
parameter.isImplicit() = true
----------
----------
Executable: A$1
Parameter at position 0
parameter.getType() = class A
parameter.isImplicit() = true
----------
----------
Executable: valueOf
Parameter at position 0
parameter.getType() = class java.lang.String
parameter.isImplicit() = true
----------
----------
Executable: A$R
Parameter at position 0
parameter.getType() = int
parameter.isImplicit() = true
Parameter at position 1
parameter.getType() = float
parameter.isImplicit() = true
----------
----------
Executable: A$E
Parameter at position 0
parameter.getType() = class java.lang.String
parameter.isSynthetic() = true
Parameter at position 1
parameter.getType() = int
parameter.isSynthetic() = true
----------
ACTUAL -
----------
Executable: A$Inner
Parameter at position 0
parameter.getType() = class A
parameter.isImplicit() = false
----------
----------
Executable: A$1
Parameter at position 0
parameter.getType() = class A
parameter.isImplicit() = false
----------
----------
Executable: valueOf
Parameter at position 0
parameter.getType() = class java.lang.String
parameter.isImplicit() = false
----------
----------
Executable: A$R
Parameter at position 0
parameter.getType() = int
parameter.isImplicit() = false
Parameter at position 1
parameter.getType() = float
parameter.isImplicit() = false
----------
----------
Executable: A$E
Parameter at position 0
parameter.getType() = class java.lang.String
parameter.isSynthetic() = false
Parameter at position 1
parameter.getType() = int
parameter.isSynthetic() = false
----------

---------- BEGIN SOURCE ----------
import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;

public class A {
  public static void main(String[] args) throws Exception {
    // implicit
    print(Inner.class.getDeclaredConstructors()[0], true);
    print(A.class.getDeclaredField("anon").get(new A()).getClass().getDeclaredConstructors()[0], true);
    print(E.class.getDeclaredMethod("valueOf", String.class), true);
    print(R.class.getDeclaredConstructors()[0], true);
    // synthetic
    print(E.class.getDeclaredConstructors()[0], false);
  }
  static void print(Executable executable, boolean implicit) {
    System.out.println("----------");
    System.out.println("Executable: " + executable.getName());
    Parameter[] parameters = executable.getParameters();
    for (int i = 0; i < parameters.length; i++) {
      Parameter parameter = parameters[i];
      System.out.println("Parameter at position " + i);
      System.out.println("parameter.getType() = " + parameter.getType());
      if (implicit) {
        System.out.println("parameter.isImplicit() = " + parameter.isImplicit());
      } else {
        System.out.println("parameter.isSynthetic() = " + parameter.isSynthetic());
      }
    }
    System.out.println("----------");
  }
  class Inner {
    Inner() {}
  }
  Inner anon = new Inner() {};
  enum E {}
  record R(int a, float b) {
    public R {}
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use the -parameters flag for compilation (does not work for the record ctor).

FREQUENCY : always



Comments
[~cstein] it seems like this could be fixed by backporting JDK-8058322 to jdk8. WDYT?
29-04-2024

This change led to a regression JUnit 5.11.0-M1 in Java 8. Note that JUnit 5.11 is built with JDK 21 and `--release 8`. Find details at https://github.com/junit-team/junit5/issues/3797 The work-around is to also pass `-parameters` to the compiler arguments to have non-empty names.
29-04-2024

Changeset: b3dbf28b Author: Hannes Greule <SirYwell@users.noreply.github.com> Committer: Julian Waters <jwaters@openjdk.org> Date: 2023-04-30 07:34:09 +0000 URL: https://git.openjdk.org/jdk/commit/b3dbf28bc0614bee2f7137af95389134155c9511
30-04-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/9862 Date: 2022-08-12 19:54:59 +0000
12-08-2022

Oddity is observed in Parameter#isImplicit() method. As per its documentation, it returns true if the parameter is implicitly declared in source code. The attached reproducer however outputs parameter.isImplicit() = false Additional context provided by the submitter: https://mail.openjdk.org/pipermail/compiler-dev/2022-May/019783.html https://mail.openjdk.org/pipermail/compiler-dev/2022-June/019924.html
12-08-2022