Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion src/hotspot/share/classfile/javaClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
28 changes: 17 additions & 11 deletions src/java.base/share/classes/java/lang/Class.java
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ private Class(ClassLoader loader, Class<?> arrayComponentType, char mods, Protec
modifiers = mods;
protectionDomain = pd;
primitive = isPrim;
rawAccessFlags = flags;
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 @@ -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
Expand Down Expand Up @@ -1381,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 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);
}

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


Expand Down
44 changes: 35 additions & 9 deletions test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java
Original file line number Diff line number Diff line change
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("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<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