diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 2dcfc43898c4b..a6452523db9c2 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,10 @@ 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. + // 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()); assert(oop_size(mirror()) == mk->instance_size(k), "should have been set"); @@ -1378,6 +1383,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 +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, "classFileAccessFlags", 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 +1572,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..f358729dfb2d0 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1981,13 +1981,11 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls, "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..20a4680b0e735 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; + classFileAccessFlags = 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 classFileAccessFlags; // Set by the VM private final transient boolean primitive; // Set by the VM if the Class is a primitive type. // package-private @@ -1379,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 getClassAccessFlagsRaw 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) ? - getClassAccessFlagsRaw() : getModifiers(), location, this); + getClassFileAccessFlags() : getModifiers(), location, this); } /** @@ -4130,17 +4132,16 @@ 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. - */ - private int getClassAccessFlagsRaw() { - Class c = isArray() ? elementType() : this; - return c.getClassAccessFlagsRaw0(); - } - - private native int getClassAccessFlagsRaw0(); + /** + * 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. + */ + 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 0175558d31348..1d62d69889603 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 getClassFileAccessFlags(Class klass) { + return klass.getClassFileAccessFlags(); + } 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..efa36b5b2d8fc 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 getClassFileAccessFlags(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..d39fd9231da88 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,9 @@ 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) { + return SharedSecrets.getJavaLangAccess().getClassFileAccessFlags(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..ee3531868e6e5 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 @@ -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("getClassAccessFlagsRaw", 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"); 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());