8364187: Make getClassAccessFlagsRaw non-native

Reviewed-by: thartmann, rriggs, liach
This commit is contained in:
Coleen Phillimore 2025-08-01 15:21:45 +00:00
parent 2ba8a06f0c
commit ee3665bca0
16 changed files with 93 additions and 68 deletions

View File

@ -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<Klass*>* 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

View File

@ -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);

View File

@ -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") \
\

View File

@ -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:

View File

@ -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;

View File

@ -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());
}

View File

@ -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));

View File

@ -240,7 +240,7 @@ public final class Class<T> implements java.io.Serializable,
* 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.
@ -249,6 +249,7 @@ public final class Class<T> implements java.io.Serializable,
modifiers = mods;
protectionDomain = pd;
primitive = isPrim;
classFileAccessFlags = flags;
}
/**
@ -1012,6 +1013,7 @@ public final class Class<T> implements java.io.Serializable,
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
@ -1383,13 +1385,13 @@ public final class Class<T> implements java.io.Serializable,
// 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);
}
/**
@ -4134,17 +4136,16 @@ public final class Class<T> implements java.io.Serializable,
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;
}
}

View File

@ -2022,6 +2022,9 @@ public final class System {
public byte[] getRawExecutableTypeAnnotations(Executable executable) {
return Class.getExecutableTypeAnnotationBytes(executable);
}
public int getClassFileAccessFlags(Class<?> klass) {
return klass.getClassFileAccessFlags();
}
public <E extends Enum<E>>
E[] getEnumConstantsShared(Class<E> klass) {
return klass.getEnumConstantsShared();

View File

@ -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;

View File

@ -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);
}
/**

View File

@ -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

View File

@ -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)
{

View File

@ -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 class ClassAccessFlagsRawTest {
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 object array. should return flags of component.
flags = (int)m.invoke((new SUPERnotset[2]).getClass());
if (flags != Modifier.PUBLIC) {
// Test that primitive array raw access flags return 0.
flags = (int)m.invoke(arr.getClass());
if (flags != 0) {
throw new RuntimeException(
"expected 0x1, got 0x" + Integer.toHexString(flags) + " for object array");
"expected 0x0 got 0x" + Integer.toHexString(flags) + " for primitive array");
}
// test multi-dimensional object array. should return flags of component.
flags = (int)m.invoke((new SUPERnotset[4][2]).getClass());
// 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<AccessFlag> 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 != 0) {
throw new RuntimeException(
"expected 0x0, got 0x" + Integer.toHexString(flags) + " for object array");
}
// 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");

View File

@ -148,7 +148,8 @@ public class ModuleSetAccessibleTest {
// 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 {

View File

@ -194,7 +194,8 @@ public class TrySetAccessibleTest {
// 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());