diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 0c54c2db4e0..6c783903ae4 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -739,6 +739,12 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { } } +bool InstanceKlass::is_record() const { + return _record_components != NULL && + is_final() && + java_super() == SystemDictionary::Record_klass(); +} + bool InstanceKlass::is_sealed() const { return _permitted_subclasses != NULL && _permitted_subclasses != Universe::the_empty_short_array(); diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 5e1da7c4f95..676efd90dda 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -473,7 +473,7 @@ class InstanceKlass: public Klass { void set_record_components(Array* record_components) { _record_components = record_components; } - bool is_record() const { return _record_components != NULL; } + bool is_record() const; // permitted subclasses Array* permitted_subclasses() const { return _permitted_subclasses; } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 8f9464add4f..01ac5905a51 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -1851,6 +1851,9 @@ JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, } JVM_END + +// A class is a record if and only if it is final and a direct subclass of +// java.lang.Record and has a Record attribute; otherwise, it is not a record. JVM_ENTRY(jboolean, JVM_IsRecord(JNIEnv *env, jclass cls)) { JVMWrapper("JVM_IsRecord"); @@ -1864,6 +1867,11 @@ JVM_ENTRY(jboolean, JVM_IsRecord(JNIEnv *env, jclass cls)) } JVM_END +// Returns an array containing the components of the Record attribute, +// or NULL if the attribute is not present. +// +// Note that this function returns the components of the Record attribute +// even if the class is not a record. JVM_ENTRY(jobjectArray, JVM_GetRecordComponents(JNIEnv* env, jclass ofClass)) { JVMWrapper("JVM_GetRecordComponents"); @@ -1871,31 +1879,26 @@ JVM_ENTRY(jobjectArray, JVM_GetRecordComponents(JNIEnv* env, jclass ofClass)) assert(c->is_instance_klass(), "must be"); InstanceKlass* ik = InstanceKlass::cast(c); - if (ik->is_record()) { - Array* components = ik->record_components(); - assert(components != NULL, "components should not be NULL"); - { - JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp(THREAD, ik->constants()); - int length = components->length(); - assert(length >= 0, "unexpected record_components length"); - objArrayOop record_components = - oopFactory::new_objArray(SystemDictionary::RecordComponent_klass(), length, CHECK_NULL); - objArrayHandle components_h (THREAD, record_components); + Array* components = ik->record_components(); + if (components != NULL) { + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp(THREAD, ik->constants()); + int length = components->length(); + assert(length >= 0, "unexpected record_components length"); + objArrayOop record_components = + oopFactory::new_objArray(SystemDictionary::RecordComponent_klass(), length, CHECK_NULL); + objArrayHandle components_h (THREAD, record_components); - for (int x = 0; x < length; x++) { - RecordComponent* component = components->at(x); - assert(component != NULL, "unexpected NULL record component"); - oop component_oop = java_lang_reflect_RecordComponent::create(ik, component, CHECK_NULL); - components_h->obj_at_put(x, component_oop); - } - return (jobjectArray)JNIHandles::make_local(THREAD, components_h()); + for (int x = 0; x < length; x++) { + RecordComponent* component = components->at(x); + assert(component != NULL, "unexpected NULL record component"); + oop component_oop = java_lang_reflect_RecordComponent::create(ik, component, CHECK_NULL); + components_h->obj_at_put(x, component_oop); } + return (jobjectArray)JNIHandles::make_local(THREAD, components_h()); } - // Return empty array if ofClass is not a record. - objArrayOop result = oopFactory::new_objArray(SystemDictionary::RecordComponent_klass(), 0, CHECK_NULL); - return (jobjectArray)JNIHandles::make_local(THREAD, result); + return NULL; } JVM_END diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 51577044455..16d5dc6e5dc 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -2383,11 +2383,7 @@ public final class Class implements java.io.Serializable, if (!isRecord()) { return null; } - RecordComponent[] recordComponents = getRecordComponents0(); - if (recordComponents == null) { - return new RecordComponent[0]; - } - return recordComponents; + return getRecordComponents0(); } /** @@ -3577,9 +3573,17 @@ public final class Class implements java.io.Serializable, private native Field[] getDeclaredFields0(boolean publicOnly); private native Method[] getDeclaredMethods0(boolean publicOnly); private native Constructor[] getDeclaredConstructors0(boolean publicOnly); - private native Class[] getDeclaredClasses0(); + private native Class[] getDeclaredClasses0(); + + /* + * Returns an array containing the components of the Record attribute, + * or null if the attribute is not present. + * + * Note that this method returns non-null array on a class with + * the Record attribute even if this class is not a record. + */ private native RecordComponent[] getRecordComponents0(); - private native boolean isRecord0(); + private native boolean isRecord0(); /** * Helper method to get the method name from arguments. @@ -3706,6 +3710,8 @@ public final class Class implements java.io.Serializable, * @since 16 */ public boolean isRecord() { + // this superclass and final modifier check is not strictly necessary + // they are intrinsified and serve as a fast-path check return getSuperclass() == java.lang.Record.class && (this.getModifiers() & Modifier.FINAL) != 0 && isRecord0();