Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/hotspot/share/classfile/javaClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/classfile/javaClasses.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 0 additions & 2 deletions src/hotspot/share/classfile/vmIntrinsics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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") \
\
Expand Down
1 change: 0 additions & 1 deletion src/hotspot/share/opto/c2compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 1 addition & 11 deletions src/hotspot/share/opto/library_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 0 additions & 2 deletions src/hotspot/share/opto/memnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
13 changes: 0 additions & 13 deletions src/hotspot/share/prims/jvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1741,19 +1741,6 @@ JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofC
}
JVM_END

JVM_ENTRY(jint, JVM_GetClassAccessFlags(JNIEnv *env, jclass cls))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the declaration in jvm.h?

JVM_GetClassAccessFlags(JNIEnv *env, jclass cls);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I missed the declaration. Will fix it. Thanks for filing the bug Roger.

{
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));
Expand Down
33 changes: 17 additions & 16 deletions src/java.base/share/classes/java/lang/Class.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -245,6 +245,7 @@ private Class(ClassLoader loader, Class<?> arrayComponentType, char mods, Protec
modifiers = mods;
protectionDomain = pd;
primitive = isPrim;
classFileAccessFlags = flags;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not that hard to add a field, why not have done this for identity?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fields are initialized by injection in javaClasses instead of through this constructor, so it's easy for us to accidentally forget to inject a field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For IDENTITY, I didn't have to inject that one because the Java code knew when to set it, not the JVM code reading the data out of the classfile. And the logic belongs in the Java code, not the JVM. This one comes from the classfile, there isn't another way to get the information to the java.lang.Class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The VM and Java use the same logic for the value of isIdentity().
It is computed from as many a 5 fields/flags. At present, it has to be computed on each call to Class.isIdentity().
It would be reasonable to compute the value once in the constructor, but the code in the constructor is not run.

Copy link
Contributor Author

@coleenp coleenp Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roger, can you file another RFE for this for repo-valhalla and I can try to figure out how to best to do this? Or I can. I didn't think the valhalla isIdentityClass() code was expensive to call.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created JDK-8364447

}

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1379,13 +1381,13 @@ public Set<AccessFlag> 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);
}

/**
Expand Down Expand Up @@ -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;
}
}
3 changes: 3 additions & 0 deletions src/java.base/share/classes/java/lang/System.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 extends Enum<E>>
E[] getEnumConstantsShared(Class<E> klass) {
return klass.getEnumConstantsShared();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}


/**
Expand Down
1 change: 0 additions & 1 deletion src/java.base/share/native/libjava/Class.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions src/java.base/share/native/libjava/Reflection.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
{
Expand Down
46 changes: 36 additions & 10 deletions test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -32,6 +32,7 @@
*/

import java.lang.reflect.*;
import java.util.Set;

public class ClassAccessFlagsRawTest {

Expand All @@ -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<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 != 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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down