JDK-8301663 : java.lang.reflect.Field.get(Runnable.class) throws IllegalArgumentException
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 18,19,19.0.2
  • Priority: P3
  • Status: Resolved
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2023-01-31
  • Updated: 2024-08-01
  • Resolved: 2023-02-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.
JDK 20
20 b03Resolved
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
# Java version
java version "19.0.2" 2023-01-17
Java(TM) SE Runtime Environment (build 19.0.2+7-44)
Java HotSpot(TM) 64-Bit Server VM (build 19.0.2+7-44, mixed mode, sharing)

# Operating system details
$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS"
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

$ uname -a
Linux luzhou 5.4.0-86-generic #97-Ubuntu SMP Fri Sep 17 19:19:40 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
Different outputs of the attached program are observed between
different compilation tiers. This bug affects Oracle JDK 18.0.2.1 and
Oracle JDK 19.0.2. It was not reproduced in Oracle JDK 11.0.18 and
17.0.6.

REGRESSION : Last worked in version 17.0.6

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The following steps shows how to reproduce the bug on Java 19.0.2 in a                                                                            
Ubuntu Linux environment.

# Compile
$ javac C.java

# Default or compilation up to level 4
$ java --add-opens java.base/java.lang=ALL-UNNAMED C
# Output (non-deterministic)
8036

# Compilation up to level 1
$ java --add-opens java.base/java.lang=ALL-UNNAMED -XX:TieredStopAtLevel=1 C
# Output
100000

# Interpreter or compilation up to level 0
$ java --add-opens java.base/java.lang=ALL-UNNAMED -Xint C
# Output (correct)
100000


---------- BEGIN SOURCE ----------
# C.java
import java.lang.reflect.Field;
public class C {

    public static void main(String[] args) throws Exception {
        int count = 0;
        for (int i = 0; i < 100_000; ++i) {
            try {
                Field f = Class.class.getDeclaredField("componentType");
                f.setAccessible(true);
                Object val = f.get(Runnable.class);
            } catch (IllegalArgumentException e) {
                count += 1;
            }
        }
        System.out.println(count);
    }
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
Only `componentType` field has this issue. Field::get returns the value of all other fields in Class.class correctly. This is internal implementation and the issue has been fixed in JDK 20. This can be closed as a dup of JDK-8288064.
02-02-2023

The `componentType` field is non-null for non-array Class objects which is set the class init lock prior to JDK-8288064. // Init lock is a C union with component_mirror. Only instanceKlass mirrors have // init_lock and only ArrayKlass mirrors have component_mirror. Since both are oops // GC treats them the same. _init_lock_offset = _component_mirror_offset; The init lock is int[]. The hotspot implementation (java_lang_Class::initialize_mirror_fields): typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK); set_init_lock(mirror(), r); With JDK-8288064, the `componentType` field is always null that explains why this becomes a non-issue. The `componentType` of Runnable.class is an int array and hence ClassCastException was thrown when VarHandle is used to get the field value. Exception in thread "main" java.lang.IllegalArgumentException: Can not get final java.lang.Class field java.lang.Class.componentType on java.lang.Class at java.base/jdk.internal.reflect.MethodHandleFieldAccessorImpl.newGetIllegalArgumentException(MethodHandleFieldAccessorImpl.java:89) at java.base/jdk.internal.reflect.MethodHandleObjectFieldAccessorImpl.get(MethodHandleObjectFieldAccessorImpl.java:61) at java.base/java.lang.reflect.Field.get(Field.java:428) at C.main(C.java:12) Caused by: java.lang.ClassCastException: Cannot cast [I to java.lang.Class at java.base/java.lang.Class.cast(Class.java:3946) at java.base/java.lang.invoke.DirectMethodHandle$Accessor.checkCast(DirectMethodHandle.java:516) at java.base/java.lang.invoke.DirectMethodHandle.checkCast(DirectMethodHandle.java:599) at java.base/jdk.internal.reflect.MethodHandleObjectFieldAccessorImpl.get(MethodHandleObjectFieldAccessorImpl.java:57)
02-02-2023

Core reflection Field.get, Method.invoke, ... was reimplemented in JDK 18 to use method handles. I think this issue is related and may have been fixed by the JDK-8300924 in the main line. It can be worked around by running with -Djdk.reflect.useDirectMethodHandle=false.
02-02-2023

Simplifying C.java to: import java.lang.reflect.Field; public class C { public static void main(String[] args) throws Exception { Field f = Class.class.getDeclaredField("componentType"); f.setAccessible(true); Object val = f.get(Runnable.class); } } We should not throw an IllegalArgumentException because "componentType" is indeed a field of class Class and we call get() with a valid object Runnable.class of type Class. Running the simplified C.java above with the following command: $ java --add-opens java.base/java.lang=ALL-UNNAMED -Xint C.java Results: - JDK 17+35: No exception - JDK 18+36: Throws IllegalArgumentException - JDK 19+36: Throws IllegalArgumentException - JDK 20+34: No exception I've tried to trace it back to the point where it was introduced and where it was fixed: - Introduced in JDK-8271820 (JEP 416: Reimplement Core Reflection with Method Handle) - Starts to work again with JDK-8288064 (I'm not sure how this runtime change by [~coleenp] fixes this - could be unrelated). This suggests that it is not a compiler issue. Moving to core-libs for further analysis.
02-02-2023

Issue is reproduced. Different outputs are observed between different compilation tiers. Submitter mentions that issue is not observed in JDK 17 or 11 but I am observing output to be 0 for all other JDKs except 19.0.2 OS: Windows 10 JDK 11.0.18 : Fail Output: 0 JDK 17.0.6 : Fail Output: 0 JDK 19.0.2 : Fail Output: java C --> 28964, java -XX:TieredStopAtLevel=1 C -->100000, java -Xint ->100000 JDK 20ea33: Fail Output: 0 JDK 21ea7: Fail Output: 0 Moving it to Dev team for further analysis
02-02-2023