From 1d17cbd6dc73271aeaae495fd689a16797e2943d Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Fri, 25 Jul 2025 12:21:46 +0000 Subject: [PATCH 1/7] 8364187: Make getClassAccessFlagsRaw non-native --- src/hotspot/share/classfile/javaClasses.cpp | 17 +++++++++++++++++ src/hotspot/share/classfile/javaClasses.hpp | 4 ++++ src/hotspot/share/classfile/vmIntrinsics.hpp | 2 -- src/hotspot/share/opto/c2compiler.cpp | 1 - src/hotspot/share/opto/library_call.cpp | 12 +----------- src/hotspot/share/opto/memnode.cpp | 7 ------- src/hotspot/share/prims/jvm.cpp | 13 ------------- .../share/classes/java/lang/Class.java | 19 +++++++++---------- .../share/classes/java/lang/System.java | 3 +++ .../jdk/internal/access/JavaLangAccess.java | 5 +++++ .../jdk/internal/reflect/Reflection.java | 8 ++++++-- src/java.base/share/native/libjava/Class.c | 1 - .../share/native/libjava/Reflection.c | 8 +------- .../ClassFile/ClassAccessFlagsRawTest.java | 4 ++-- .../ModuleSetAccessibleTest.java | 3 ++- .../TrySetAccessibleTest.java | 3 ++- 16 files changed, 52 insertions(+), 58 deletions(-) diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 2dcfc43898c4b..af451884162c2 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -870,6 +870,7 @@ int java_lang_Class::_classRedefinedCount_offset; int java_lang_Class::_reflectionData_offset; int java_lang_Class::_modifiers_offset; int java_lang_Class::_is_primitive_offset; +int java_lang_Class::_raw_access_flags_offset; bool java_lang_Class::_offsets_computed = false; GrowableArray* java_lang_Class::_fixup_mirror_list = nullptr; @@ -1073,6 +1074,9 @@ void java_lang_Class::allocate_mirror(Klass* k, bool is_scratch, Handle protecti // Set the modifiers flag. u2 computed_modifiers = k->compute_modifier_flags(); set_modifiers(mirror(), computed_modifiers); + // Set the raw access_flags, this is used by reflection instead of modifier flags. + assert(!k->is_array_klass() || k->access_flags().as_unsigned_short() == 0, "isn't set for arrays or I'm going nuts"); + set_raw_access_flags(mirror(), k->access_flags().as_unsigned_short()); InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); assert(oop_size(mirror()) == mk->instance_size(k), "should have been set"); @@ -1378,6 +1382,8 @@ oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, Basic assert(static_oop_field_count(java_class) == 0, "should have been zeroed by allocation"); #endif set_modifiers(java_class, JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); + set_raw_access_flags(java_class, JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); + set_is_primitive(java_class); return java_class; } @@ -1519,6 +1525,7 @@ oop java_lang_Class::primitive_mirror(BasicType t) { macro(_reflectionData_offset, k, "reflectionData", java_lang_ref_SoftReference_signature, false); \ macro(_signers_offset, k, "signers", object_array_signature, false); \ macro(_modifiers_offset, k, vmSymbols::modifiers_name(), char_signature, false); \ + macro(_raw_access_flags_offset, k, "rawAccessFlags", char_signature, false); \ macro(_protection_domain_offset, k, "protectionDomain", java_security_ProtectionDomain_signature, false); \ macro(_is_primitive_offset, k, "primitive", bool_signature, false); @@ -1564,6 +1571,16 @@ void java_lang_Class::set_modifiers(oop the_class_mirror, u2 value) { the_class_mirror->char_field_put(_modifiers_offset, value); } +int java_lang_Class::raw_access_flags(oop the_class_mirror) { + assert(_raw_access_flags_offset != 0, "offsets should have been initialized"); + return the_class_mirror->char_field(_raw_access_flags_offset); +} + +void java_lang_Class::set_raw_access_flags(oop the_class_mirror, u2 value) { + assert(_raw_access_flags_offset != 0, "offsets should have been initialized"); + the_class_mirror->char_field_put(_raw_access_flags_offset, value); +} + // Note: JDK1.1 and before had a privateInfo_offset field which was used for the // platform thread structure, and a eetop offset which was used for thread diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 37ca22e92957b..faa325d55dd5d 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -258,6 +258,7 @@ class java_lang_Class : AllStatic { static int _reflectionData_offset; static int _modifiers_offset; static int _is_primitive_offset; + static int _raw_access_flags_offset; static bool _offsets_computed; @@ -342,6 +343,9 @@ class java_lang_Class : AllStatic { static int modifiers(oop java_class); static void set_modifiers(oop java_class, u2 value); + static int raw_access_flags(oop java_class); + static void set_raw_access_flags(oop java_class, u2 value); + static size_t oop_size(oop java_class); static void set_oop_size(HeapWord* java_class, size_t size); static int static_oop_field_count(oop java_class); diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 6699d99e95b94..ed737cf60c24e 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -314,8 +314,6 @@ class methodHandle; do_intrinsic(_Class_cast, java_lang_Class, Class_cast_name, object_object_signature, F_R) \ do_name( Class_cast_name, "cast") \ \ - do_intrinsic(_getClassAccessFlags, reflect_Reflection, getClassAccessFlags_name, class_int_signature, F_SN) \ - do_name( getClassAccessFlags_name, "getClassAccessFlags") \ do_intrinsic(_getLength, java_lang_reflect_Array, getLength_name, object_int_signature, F_SN) \ do_name( getLength_name, "getLength") \ \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index e5b1e131505c4..5aa7e4f972738 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -760,7 +760,6 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_isInstance: case vmIntrinsics::_isHidden: case vmIntrinsics::_getSuperclass: - case vmIntrinsics::_getClassAccessFlags: case vmIntrinsics::_floatToRawIntBits: case vmIntrinsics::_floatToIntBits: case vmIntrinsics::_intBitsToFloat: diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 818e28a310655..90830ddeefc73 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -519,8 +519,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_isInstance: case vmIntrinsics::_isHidden: - case vmIntrinsics::_getSuperclass: - case vmIntrinsics::_getClassAccessFlags: return inline_native_Class_query(intrinsic_id()); + case vmIntrinsics::_getSuperclass: return inline_native_Class_query(intrinsic_id()); case vmIntrinsics::_floatToRawIntBits: case vmIntrinsics::_floatToIntBits: @@ -4007,10 +4006,6 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { prim_return_value = null(); return_type = TypeInstPtr::MIRROR->cast_to_ptr_type(TypePtr::BotPTR); break; - case vmIntrinsics::_getClassAccessFlags: - prim_return_value = intcon(JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); - return_type = TypeInt::CHAR; - break; default: fatal_unexpected_iid(id); break; @@ -4106,11 +4101,6 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { } break; - case vmIntrinsics::_getClassAccessFlags: - p = basic_plus_adr(kls, in_bytes(Klass::access_flags_offset())); - query_value = make_load(nullptr, p, TypeInt::CHAR, T_CHAR, MemNode::unordered); - break; - default: fatal_unexpected_iid(id); break; diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 1da8f42935911..1a3dfdd119244 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1979,15 +1979,8 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls, ciKlass* klass) const { assert(!UseCompactObjectHeaders || tkls->offset() != in_bytes(Klass::prototype_header_offset()), "must not happen"); - if (tkls->offset() == in_bytes(Klass::access_flags_offset())) { - // The field is Klass::_access_flags. Return its (constant) value. - // (Folds up the 2nd indirection in Reflection.getClassAccessFlags(aClassConstant).) - assert(Opcode() == Op_LoadUS, "must load an unsigned short from _access_flags"); - return TypeInt::make(klass->access_flags()); - } if (tkls->offset() == in_bytes(Klass::misc_flags_offset())) { // The field is Klass::_misc_flags. Return its (constant) value. - // (Folds up the 2nd indirection in Reflection.getClassAccessFlags(aClassConstant).) assert(Opcode() == Op_LoadUB, "must load an unsigned byte from _misc_flags"); return TypeInt::make(klass->misc_flags()); } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 13d89b396fa40..52cac8c11dd28 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -1741,19 +1741,6 @@ JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofC } JVM_END -JVM_ENTRY(jint, JVM_GetClassAccessFlags(JNIEnv *env, jclass cls)) -{ - oop mirror = JNIHandles::resolve_non_null(cls); - if (java_lang_Class::is_primitive(mirror)) { - // Primitive type - return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; - } - - Klass* k = java_lang_Class::as_Klass(mirror); - return k->access_flags().as_class_flags(); -} -JVM_END - JVM_ENTRY(jboolean, JVM_AreNestMates(JNIEnv *env, jclass current, jclass member)) { Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current)); diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index a7b856fa449d4..3c4fd43b890a4 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -236,7 +236,7 @@ private static void runtimeSetup() { * This constructor is not used and prevents the default constructor being * generated. */ - private Class(ClassLoader loader, Class arrayComponentType, char mods, ProtectionDomain pd, boolean isPrim) { + private Class(ClassLoader loader, Class arrayComponentType, char mods, ProtectionDomain pd, boolean isPrim, char flags) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. // The following assignments are done directly by the VM without calling this constructor. @@ -245,6 +245,7 @@ private Class(ClassLoader loader, Class arrayComponentType, char mods, Protec modifiers = mods; protectionDomain = pd; primitive = isPrim; + rawAccessFlags = flags; } /** @@ -1008,6 +1009,7 @@ public Module getModule() { private transient Object classData; // Set by VM private transient Object[] signers; // Read by VM, mutable private final transient char modifiers; // Set by the VM + private final transient char rawAccessFlags; // Set by the VM private final transient boolean primitive; // Set by the VM if the Class is a primitive type. // package-private @@ -1385,7 +1387,7 @@ public Set accessFlags() { AccessFlag.Location.INNER_CLASS : AccessFlag.Location.CLASS; return getReflectionFactory().parseAccessFlags((location == AccessFlag.Location.CLASS) ? - getClassAccessFlagsRaw() : getModifiers(), location, this); + getRawClassAccessFlags() : getModifiers(), location, this); } /** @@ -4130,17 +4132,14 @@ int getClassFileVersion() { private native int getClassFileVersion0(); - /* - * Return the access flags as they were in the class's bytecode, including - * the original setting of ACC_SUPER. + /** + * {@return the access flags as they were in the class's bytecode, including + * the original setting of ACC_SUPER} * * If the class is an array type then the access flags of the element type is * returned. If the class is a primitive then ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC. */ - private int getClassAccessFlagsRaw() { - Class c = isArray() ? elementType() : this; - return c.getClassAccessFlagsRaw0(); + int getRawClassAccessFlags() { + return isArray() ? elementType().getRawClassAccessFlags() : rawAccessFlags; } - - private native int getClassAccessFlagsRaw0(); } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 0175558d31348..f9bca6e8a54c6 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2022,6 +2022,9 @@ public byte[] getRawClassTypeAnnotations(Class klass) { public byte[] getRawExecutableTypeAnnotations(Executable executable) { return Class.getExecutableTypeAnnotationBytes(executable); } + public int getRawClassAccessFlags(Class klass) { + return klass.getRawClassAccessFlags(); + } public > E[] getEnumConstantsShared(Class klass) { return klass.getEnumConstantsShared(); diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index e8343274caca7..a79f04bc509e2 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -112,6 +112,11 @@ public interface JavaLangAccess { */ byte[] getRawExecutableTypeAnnotations(Executable executable); + /** + * Get the int value of the Class's class-file access flags. + */ + int getRawClassAccessFlags(Class klass); + /** * Returns the elements of an enum class or null if the * Class object does not represent an enum type; diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java index 5025b81dd10ed..691f92d608919 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -81,8 +81,12 @@ public class Reflection { to compatibility reasons; see 4471811. Only the values of the low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be valid. */ - @IntrinsicCandidate - public static native int getClassAccessFlags(Class c); + public static int getClassAccessFlags(Class c) { + class Holder { + static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + } + return Holder.JLA.getRawClassAccessFlags(c); + } /** diff --git a/src/java.base/share/native/libjava/Class.c b/src/java.base/share/native/libjava/Class.c index 3ab3e764bff8d..9fb348d9217e4 100644 --- a/src/java.base/share/native/libjava/Class.c +++ b/src/java.base/share/native/libjava/Class.c @@ -75,7 +75,6 @@ static JNINativeMethod methods[] = { {"isRecord0", "()Z", (void *)&JVM_IsRecord}, {"getPermittedSubclasses0", "()[" CLS, (void *)&JVM_GetPermittedSubclasses}, {"getClassFileVersion0", "()I", (void *)&JVM_GetClassFileVersion}, - {"getClassAccessFlagsRaw0", "()I", (void *)&JVM_GetClassAccessFlags}, }; #undef OBJ diff --git a/src/java.base/share/native/libjava/Reflection.c b/src/java.base/share/native/libjava/Reflection.c index d6722feb7d195..d556ee9231019 100644 --- a/src/java.base/share/native/libjava/Reflection.c +++ b/src/java.base/share/native/libjava/Reflection.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,12 +33,6 @@ Java_jdk_internal_reflect_Reflection_getCallerClass(JNIEnv *env, jclass unused) return JVM_GetCallerClass(env); } -JNIEXPORT jint JNICALL -Java_jdk_internal_reflect_Reflection_getClassAccessFlags(JNIEnv *env, jclass unused, jclass cls) -{ - return JVM_GetClassAccessFlags(env, cls); -} - JNIEXPORT jboolean JNICALL Java_jdk_internal_reflect_Reflection_areNestMates(JNIEnv *env, jclass unused, jclass current, jclass member) { diff --git a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java index 687bdf2ef57aa..1ffe61294702e 100644 --- a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java +++ b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ public static void testIt(String className, int expectedResult) throws Exception public static void main(String argv[]) throws Throwable { Class cl = java.lang.Class.class; - m = cl.getDeclaredMethod("getClassAccessFlagsRaw", new Class[0]); + m = cl.getDeclaredMethod("getRawClassAccessFlags", new Class[0]); m.setAccessible(true); testIt("SUPERset", 0x21); // ACC_SUPER 0x20 + ACC_PUBLIC 0x1 diff --git a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java index 8ee5179ea818e..45cebdb552bee 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java @@ -148,7 +148,8 @@ public void testJavaLangClass() throws Exception { // non-public constructor Constructor ctor - = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, ProtectionDomain.class, boolean.class); + = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, + ProtectionDomain.class, boolean.class, char.class); AccessibleObject[] ctors = { ctor }; try { diff --git a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java index 9574afee40729..774ee4d1dad94 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java @@ -194,7 +194,8 @@ public void testJavaLangClass() throws Exception { // non-public constructor Constructor ctor - = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, ProtectionDomain.class, boolean.class); + = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, + ProtectionDomain.class, boolean.class, char.class); AccessibleObject[] ctors = { ctor }; assertFalse(ctor.trySetAccessible()); From 5313fa14d0d0be4137ead069ad856084bb155f5d Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Mon, 28 Jul 2025 12:14:48 +0000 Subject: [PATCH 2/7] Add a comment --- src/hotspot/share/classfile/javaClasses.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index af451884162c2..4283ee3f53255 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1075,7 +1075,8 @@ void java_lang_Class::allocate_mirror(Klass* k, bool is_scratch, Handle protecti u2 computed_modifiers = k->compute_modifier_flags(); set_modifiers(mirror(), computed_modifiers); // Set the raw access_flags, this is used by reflection instead of modifier flags. - assert(!k->is_array_klass() || k->access_flags().as_unsigned_short() == 0, "isn't set for arrays or I'm going nuts"); + // The Java code for array classes gets the access flags from the element type. + assert(!k->is_array_klass() || k->access_flags().as_unsigned_short() == 0, "access flags are not set for arrays"); set_raw_access_flags(mirror(), k->access_flags().as_unsigned_short()); InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); From b4daf73150cdf92072f7650ab11d1d5a9091b057 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Mon, 28 Jul 2025 18:28:07 +0000 Subject: [PATCH 3/7] The only caller for getRawClassAccessFlags in Class.java doesn't call it for arrays so we really want the "raw" classfile access flags. --- src/java.base/share/classes/java/lang/Class.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 3c4fd43b890a4..680a6b2c0cdce 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -1381,7 +1381,7 @@ public Set accessFlags() { // Location.CLASS allows SUPER and AccessFlag.MODULE which // INNER_CLASS forbids. INNER_CLASS allows PRIVATE, PROTECTED, // and STATIC, which are not allowed on Location.CLASS. - // Use getClassAccessFlagsRaw to expose SUPER status. + // Use getRawClassAccessFlags to expose SUPER status. var location = (isMemberClass() || isLocalClass() || isAnonymousClass() || isArray()) ? AccessFlag.Location.INNER_CLASS : @@ -4133,13 +4133,10 @@ int getClassFileVersion() { private native int getClassFileVersion0(); /** - * {@return the access flags as they were in the class's bytecode, including - * the original setting of ACC_SUPER} - * - * If the class is an array type then the access flags of the element type is - * returned. If the class is a primitive then ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC. + * Return the access flags as they were in the class's bytecode, including + * the original setting of ACC_SUPER. */ int getRawClassAccessFlags() { - return isArray() ? elementType().getRawClassAccessFlags() : rawAccessFlags; + return rawAccessFlags; } } From 163a8e5ce084a62eaa6ef4581d8f74dc4f998314 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Tue, 29 Jul 2025 13:39:44 +0000 Subject: [PATCH 4/7] Rename getRawClassAccessFlags to getClassFileAccessFlags and fix the test to reflect what the JVM does. --- src/hotspot/share/classfile/javaClasses.cpp | 2 +- .../share/classes/java/lang/Class.java | 28 +++++++----- .../share/classes/java/lang/System.java | 4 +- .../jdk/internal/access/JavaLangAccess.java | 2 +- .../jdk/internal/reflect/Reflection.java | 2 +- .../ClassFile/ClassAccessFlagsRawTest.java | 44 +++++++++++++++---- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 4283ee3f53255..a6452523db9c2 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1526,7 +1526,7 @@ oop java_lang_Class::primitive_mirror(BasicType t) { macro(_reflectionData_offset, k, "reflectionData", java_lang_ref_SoftReference_signature, false); \ macro(_signers_offset, k, "signers", object_array_signature, false); \ macro(_modifiers_offset, k, vmSymbols::modifiers_name(), char_signature, false); \ - macro(_raw_access_flags_offset, k, "rawAccessFlags", char_signature, false); \ + macro(_raw_access_flags_offset, k, "classFileAccessFlags", char_signature, false); \ macro(_protection_domain_offset, k, "protectionDomain", java_security_ProtectionDomain_signature, false); \ macro(_is_primitive_offset, k, "primitive", bool_signature, false); diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 680a6b2c0cdce..4ec5c9492b92e 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -245,7 +245,7 @@ private Class(ClassLoader loader, Class arrayComponentType, char mods, Protec modifiers = mods; protectionDomain = pd; primitive = isPrim; - rawAccessFlags = flags; + classFileAccessFlags = flags; } /** @@ -1009,7 +1009,7 @@ public Module getModule() { private transient Object classData; // Set by VM private transient Object[] signers; // Read by VM, mutable private final transient char modifiers; // Set by the VM - private final transient char rawAccessFlags; // Set by the VM + private final transient char classFileAccessFlags; // Set by the VM private final transient boolean primitive; // Set by the VM if the Class is a primitive type. // package-private @@ -1381,13 +1381,13 @@ public Set accessFlags() { // Location.CLASS allows SUPER and AccessFlag.MODULE which // INNER_CLASS forbids. INNER_CLASS allows PRIVATE, PROTECTED, // and STATIC, which are not allowed on Location.CLASS. - // Use getRawClassAccessFlags to expose SUPER status. + // Use getClassFileAccessFlags to expose SUPER status. var location = (isMemberClass() || isLocalClass() || isAnonymousClass() || isArray()) ? AccessFlag.Location.INNER_CLASS : AccessFlag.Location.CLASS; return getReflectionFactory().parseAccessFlags((location == AccessFlag.Location.CLASS) ? - getRawClassAccessFlags() : getModifiers(), location, this); + getClassFileAccessFlags() : getModifiers(), location, this); } /** @@ -4132,11 +4132,17 @@ int getClassFileVersion() { private native int getClassFileVersion0(); - /** - * Return the access flags as they were in the class's bytecode, including - * the original setting of ACC_SUPER. - */ - int getRawClassAccessFlags() { - return rawAccessFlags; - } + /** + * Return the access flags as they were in the class's bytecode, including + * the original setting of ACC_SUPER. + * + * If this {@code Class} object represents a primitive type or + * void, the flags are {@code PUBLIC}, {@code ABSTRACT}, and + * {@code FINAL}. + * If this {@code Class} object represents an array type return 0. This + * is not called in Class but can be called with an array type in Reflection. + */ + int getClassFileAccessFlags() { + return classFileAccessFlags; + } } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index f9bca6e8a54c6..1d62d69889603 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2022,8 +2022,8 @@ public byte[] getRawClassTypeAnnotations(Class klass) { public byte[] getRawExecutableTypeAnnotations(Executable executable) { return Class.getExecutableTypeAnnotationBytes(executable); } - public int getRawClassAccessFlags(Class klass) { - return klass.getRawClassAccessFlags(); + public int getClassFileAccessFlags(Class klass) { + return klass.getClassFileAccessFlags(); } public > E[] getEnumConstantsShared(Class klass) { diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index a79f04bc509e2..efa36b5b2d8fc 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -115,7 +115,7 @@ public interface JavaLangAccess { /** * Get the int value of the Class's class-file access flags. */ - int getRawClassAccessFlags(Class klass); + int getClassFileAccessFlags(Class klass); /** * Returns the elements of an enum class or null if the diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java index 691f92d608919..2524bd40c61ec 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -85,7 +85,7 @@ public static int getClassAccessFlags(Class c) { class Holder { static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); } - return Holder.JLA.getRawClassAccessFlags(c); + return Holder.JLA.getClassFileAccessFlags(c); } diff --git a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java index 1ffe61294702e..ee3531868e6e5 100644 --- a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java +++ b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java @@ -32,6 +32,7 @@ */ import java.lang.reflect.*; +import java.util.Set; public class ClassAccessFlagsRawTest { @@ -48,28 +49,53 @@ public static void testIt(String className, int expectedResult) throws Exception public static void main(String argv[]) throws Throwable { Class cl = java.lang.Class.class; - m = cl.getDeclaredMethod("getRawClassAccessFlags", new Class[0]); + m = cl.getDeclaredMethod("getClassFileAccessFlags", new Class[0]); m.setAccessible(true); testIt("SUPERset", 0x21); // ACC_SUPER 0x20 + ACC_PUBLIC 0x1 testIt("SUPERnotset", Modifier.PUBLIC); - // test primitive array. should return ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC. - int flags = (int)m.invoke((new int[3]).getClass()); + // Test that primitive should return ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC. + int[] arr = new int[3]; + if (!arr.getClass().getComponentType().isPrimitive()) { + throw new RuntimeException("not primitive"); + } + int flags = (int)m.invoke(arr.getClass().getComponentType()); if (flags != (Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC)) { throw new RuntimeException( - "expected 0x411, got 0x" + Integer.toHexString(flags) + " for primitive array"); + "expected 0x411, got 0x" + Integer.toHexString(flags) + " for primitive type"); + } + + // Test that primitive array raw access flags return 0. + flags = (int)m.invoke(arr.getClass()); + if (flags != 0) { + throw new RuntimeException( + "expected 0x0 got 0x" + Integer.toHexString(flags) + " for primitive array"); } - // test object array. should return flags of component. + // Test that the modifier flags return element type flags. + flags = (int)arr.getClass().getModifiers(); + if (flags != (Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC)) { + throw new RuntimeException( + "expected 0x411, got 0x" + Integer.toHexString(flags) + " for primitive type"); + } + + // Test that AccessFlags set will return element type access flags. + Set aacc = arr.getClass().accessFlags(); + if (!aacc.containsAll(Set.of(AccessFlag.FINAL, AccessFlag.ABSTRACT, AccessFlag.PUBLIC))) { + throw new RuntimeException( + "AccessFlags should contain FINAL, ABSTRACT and PUBLIC for primitive type"); + } + + // Test object array. Raw access flags are 0 for arrays. flags = (int)m.invoke((new SUPERnotset[2]).getClass()); - if (flags != Modifier.PUBLIC) { + if (flags != 0) { throw new RuntimeException( - "expected 0x1, got 0x" + Integer.toHexString(flags) + " for object array"); + "expected 0x0, got 0x" + Integer.toHexString(flags) + " for object array"); } - // test multi-dimensional object array. should return flags of component. - flags = (int)m.invoke((new SUPERnotset[4][2]).getClass()); + // Test object array component type. + flags = (int)m.invoke((new SUPERnotset[2]).getClass().getComponentType()); if (flags != Modifier.PUBLIC) { throw new RuntimeException( "expected 0x1, got 0x" + Integer.toHexString(flags) + " for object array"); From a49744735b735af1a46f39d5c3e179c121b01ff1 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Tue, 29 Jul 2025 19:54:45 +0000 Subject: [PATCH 5/7] Fix comments and remove Holder. --- src/java.base/share/classes/java/lang/Class.java | 3 +-- .../share/classes/jdk/internal/reflect/Reflection.java | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 4ec5c9492b92e..20a4680b0e735 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -4139,8 +4139,7 @@ int getClassFileVersion() { * If this {@code Class} object represents a primitive type or * void, the flags are {@code PUBLIC}, {@code ABSTRACT}, and * {@code FINAL}. - * If this {@code Class} object represents an array type return 0. This - * is not called in Class but can be called with an array type in Reflection. + * If this {@code Class} object represents an array type, return 0. */ int getClassFileAccessFlags() { return classFileAccessFlags; diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java index 2524bd40c61ec..03aaab269eb51 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -82,10 +82,8 @@ public class Reflection { low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be valid. */ public static int getClassAccessFlags(Class c) { - class Holder { - static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - } - return Holder.JLA.getClassFileAccessFlags(c); + JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + return JLA.getClassFileAccessFlags(c); } From 45b92c5c02ec46461c7fdfbfd47401b2d87004da Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 30 Jul 2025 07:02:25 -0400 Subject: [PATCH 6/7] Update src/java.base/share/classes/jdk/internal/reflect/Reflection.java Co-authored-by: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> --- .../share/classes/jdk/internal/reflect/Reflection.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java index 03aaab269eb51..d39fd9231da88 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -82,8 +82,7 @@ public class Reflection { low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be valid. */ public static int getClassAccessFlags(Class c) { - JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - return JLA.getClassFileAccessFlags(c); + return SharedSecrets.getJavaLangAccess().getClassFileAccessFlags(c); } From fb6c15e88d2a99ed150eb43eb1905ce9dbecce24 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 30 Jul 2025 19:22:31 +0000 Subject: [PATCH 7/7] Restore c2 optimization. --- src/hotspot/share/opto/memnode.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 1a3dfdd119244..f358729dfb2d0 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1979,6 +1979,11 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls, ciKlass* klass) const { assert(!UseCompactObjectHeaders || tkls->offset() != in_bytes(Klass::prototype_header_offset()), "must not happen"); + if (tkls->offset() == in_bytes(Klass::access_flags_offset())) { + // The field is Klass::_access_flags. Return its (constant) value. + assert(Opcode() == Op_LoadUS, "must load an unsigned short from _access_flags"); + return TypeInt::make(klass->access_flags()); + } if (tkls->offset() == in_bytes(Klass::misc_flags_offset())) { // The field is Klass::_misc_flags. Return its (constant) value. assert(Opcode() == Op_LoadUB, "must load an unsigned byte from _misc_flags");