From bf4b5b3c473001cc6ad85ebf08bc31737df9b5ea Mon Sep 17 00:00:00 2001 From: Theresa Mammarella Date: Tue, 23 Jul 2024 16:15:55 -0400 Subject: [PATCH] Value type null restricted array support - create a null restricted array field for every RAM class that could support a flattened field (implicitly constructed) - implement JVM_IsNullRestrictedArray and JVM_NewNullRestrictedArray - Update exception thrown when null is set to a null restricted array from NullPointerException to ArrayStoreException as described in https://openjdk.org/jeps/8316779 Signed-off-by: Theresa Mammarella --- runtime/j9vm/javanextvmi.cpp | 58 ++++++++- runtime/oti/j9.h | 6 +- runtime/oti/j9nonbuilder.h | 7 ++ runtime/vm/BytecodeInterpreter.hpp | 4 +- runtime/vm/ValueTypeHelpers.hpp | 5 +- runtime/vm/classsupport.c | 6 +- runtime/vm/createramclass.cpp | 119 +++++++++++++++--- test/functional/Valhalla/playlist.xml | 15 --- .../test/lworld/ValueTypeArrayTests.java | 37 +++++- .../openj9/test/lworld/ValueTypeTests.java | 16 --- 10 files changed, 203 insertions(+), 70 deletions(-) diff --git a/runtime/j9vm/javanextvmi.cpp b/runtime/j9vm/javanextvmi.cpp index 45d6f80b6fc..fc6ff108a3e 100644 --- a/runtime/j9vm/javanextvmi.cpp +++ b/runtime/j9vm/javanextvmi.cpp @@ -763,15 +763,63 @@ JVM_IsImplicitlyConstructibleClass(JNIEnv *env, jclass cls) JNIEXPORT jboolean JNICALL JVM_IsNullRestrictedArray(JNIEnv *env, jobject obj) { - // TODO implement this with https://github.com/eclipse-openj9/openj9/issues/19460 - return JNI_FALSE; + jboolean result = JNI_FALSE; + J9VMThread *currentThread = (J9VMThread *)env; + J9InternalVMFunctions *vmFuncs = currentThread->javaVM->internalVMFunctions; + vmFuncs->internalEnterVMFromJNI(currentThread); + if (NULL == obj) { + vmFuncs->setCurrentException(currentThread, J9VMCONSTANTPOOL_JAVALANGNULLPOINTEREXCEPTION, NULL); + } else { + jclass clazz = env->GetObjectClass(obj); + J9Class *j9clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, clazz); + if (J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(j9clazz)) { + result = JNI_TRUE; + } + } + vmFuncs->internalExitVMToJNI(currentThread); + return result; } +J9Class* java_lang_Class_vmRef(JNIEnv* env, jobject clazz); + JNIEXPORT jarray JNICALL -JVM_NewNullRestrictedArray(JNIEnv *env, jclass cls, jint length) +JVM_NewNullRestrictedArray(JNIEnv *env, jclass componentType, jint length) { - assert(!"JVM_NewNullRestrictedArray unimplemented"); - return NULL; + J9VMThread *currentThread = (J9VMThread *)env; + J9JavaVM *vm = currentThread->javaVM; + J9InternalVMFunctions *vmFuncs = currentThread->javaVM->internalVMFunctions; + J9Class *ramClass = NULL; + j9object_t newArray = NULL; + jarray arrayRef = NULL; + + vmFuncs->internalEnterVMFromJNI(currentThread); + ramClass = J9VMJAVALANGCLASS_VMREF(currentThread, J9_JNI_UNWRAP_REFERENCE(componentType)); + + if (!(J9_IS_J9CLASS_VALUETYPE(ramClass) && J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(ramClass))) { + vmFuncs->setCurrentException(currentThread, J9VMCONSTANTPOOL_JAVALANGILLEGALARGUMENTEXCEPTION, NULL); + goto done; + } + + if (NULL == ramClass->nullRestrictedArrayClass) { + J9ROMArrayClass* arrayOfObjectsROMClass = (J9ROMArrayClass*)J9ROMIMAGEHEADER_FIRSTCLASS(vm->arrayROMClasses); + vmFuncs->internalCreateArrayClass(currentThread, arrayOfObjectsROMClass, ramClass); + if (NULL != currentThread->currentException) { + goto done; + } + } + + newArray = vm->memoryManagerFunctions->J9AllocateIndexableObject( + currentThread, ramClass->nullRestrictedArrayClass, length, J9_GC_ALLOCATE_OBJECT_NON_INSTRUMENTABLE); + + if (NULL == newArray) { + vmFuncs->setHeapOutOfMemoryError(currentThread); + return NULL; + } + + arrayRef = (jarray)vmFuncs->j9jni_createLocalRef(env, newArray); +done: + vmFuncs->internalExitVMToJNI(currentThread); + return arrayRef; } #endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */ diff --git a/runtime/oti/j9.h b/runtime/oti/j9.h index 0a472b748cc..0688f3fde96 100644 --- a/runtime/oti/j9.h +++ b/runtime/oti/j9.h @@ -331,7 +331,8 @@ static const struct { \ #if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) #define J9CLASS_UNPADDED_INSTANCE_SIZE(clazz) J9_VALUETYPE_FLATTENED_SIZE(clazz) -/* TODO replace with J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(clazz) J9_ARE_ALL_BITS_SET((clazz)->classFlags, J9ClassAllowsInitialDefaultValue)*/ +#define J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(clazz) J9_ARE_ALL_BITS_SET((clazz)->classFlags, J9ClassAllowsInitialDefaultValue) +/* TODO replace with J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE */ #define J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(clazz) J9_ARE_ALL_BITS_SET((clazz)->classFlags, J9ClassIsPrimitiveValueType) #define J9_IS_J9CLASS_FLATTENED(clazz) J9_ARE_ALL_BITS_SET((clazz)->classFlags, J9ClassIsFlattened) @@ -348,14 +349,17 @@ static const struct { \ J9_IS_J9CLASS_FLATTENED(fieldClazz) && \ (J9_ARE_NO_BITS_SET((romFieldShape)->modifiers, J9AccVolatile) || (J9CLASS_UNPADDED_INSTANCE_SIZE(fieldClazz) <= sizeof(U_64)))) #define J9_VALUETYPE_FLATTENED_SIZE(clazz) (J9CLASS_HAS_4BYTE_PREPADDING((clazz)) ? ((clazz)->totalInstanceSize - sizeof(U_32)) : (clazz)->totalInstanceSize) +#define J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(clazz) J9_ARE_ALL_BITS_SET((clazz)->classFlags, J9ClassArrayIsNullRestricted) #else /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ #define J9CLASS_UNPADDED_INSTANCE_SIZE(clazz) ((clazz)->totalInstanceSize) +#define J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(clazz) FALSE #define J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(clazz) FALSE #define J9_IS_J9CLASS_FLATTENED(clazz) FALSE #define J9ROMFIELD_IS_NULL_RESTRICTED(romField) FALSE #define J9_IS_FIELD_FLATTENED(fieldClazz, romFieldShape) FALSE #define J9_IS_NULL_RESTRICTED_FIELD_FLATTENED(fieldClazz, romFieldShape) FALSE #define J9_VALUETYPE_FLATTENED_SIZE(clazz)((UDATA) 0) /* It is not possible for this macro to be used since we always check J9_IS_J9CLASS_FLATTENED before ever using it. */ +#define J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(clazz) FALSE #endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ #define IS_REF_OR_VAL_SIGNATURE(firstChar) ('L' == (firstChar)) diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index a04bd591a83..00eee5be60e 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -92,6 +92,7 @@ #define J9ClassAllowsInitialDefaultValue 0x400000 #define J9ClassAllowsNonAtomicCreation 0x800000 #define J9ClassNeedToPruneMemberNames 0x1000000 +#define J9ClassArrayIsNullRestricted 0x2000000 /* @ddr_namespace: map_to_type=J9FieldFlags */ @@ -3396,6 +3397,9 @@ typedef struct J9Class { /* A linked list of weak global references to every resolved MemberName whose clazz is this class. */ J9MemberNameListNode *memberNames; #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + struct J9Class* nullRestrictedArrayClass; +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ } J9Class; /* Interface classes can never be instantiated, so the following fields in J9Class will not be used: @@ -3492,6 +3496,9 @@ typedef struct J9ArrayClass { /* A linked list of weak global references to every resolved MemberName whose clazz is this class. */ J9MemberNameListNode *memberNames; #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + struct J9Class* companionArray; +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ } J9ArrayClass; diff --git a/runtime/vm/BytecodeInterpreter.hpp b/runtime/vm/BytecodeInterpreter.hpp index 8955229cc11..e3d6b8bdd63 100644 --- a/runtime/vm/BytecodeInterpreter.hpp +++ b/runtime/vm/BytecodeInterpreter.hpp @@ -6890,8 +6890,8 @@ class INTERPRETER_CLASS } else { #if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) J9ArrayClass *arrayrefClass = (J9ArrayClass *) J9OBJECT_CLAZZ(_currentThread, arrayref); - if (J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(arrayrefClass->componentType) && (NULL == value)) { - rc = THROW_NPE; + if (J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(arrayrefClass) && (NULL == value)) { + rc = THROW_ARRAY_STORE; goto done; } #endif /* if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ diff --git a/runtime/vm/ValueTypeHelpers.hpp b/runtime/vm/ValueTypeHelpers.hpp index 32490af20e6..47059f769c9 100644 --- a/runtime/vm/ValueTypeHelpers.hpp +++ b/runtime/vm/ValueTypeHelpers.hpp @@ -512,7 +512,6 @@ class VM_ValueTypeHelpers { I_32 srcEndIndex = srcIndex + lengthInSlots; J9Class *srcClazz = J9OBJECT_CLAZZ(currentThread, srcObject); J9Class *destClazz = J9OBJECT_CLAZZ(currentThread, destObject); - J9Class *destComponentClass = ((J9ArrayClass *)destClazz)->componentType; /* Array elements must be copied backwards if source and destination overlap in memory and source is before destination */ if ((srcObject == destObject) && (srcIndex < destIndex) && ((srcIndex + lengthInSlots) > destIndex)) { @@ -566,8 +565,8 @@ class VM_ValueTypeHelpers { } if (typeChecksRequired) { - if (J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(destComponentClass) && (NULL == copyObject)) { - /* Null objects cannot be stored in an array of primitive value types */ + if (J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(destClazz) && (NULL == copyObject)) { + /* Null objects cannot be stored in an array of null-restricted value types */ return -2; } if (!VM_VMHelpers::objectArrayStoreAllowed(currentThread, destObject, copyObject)) { diff --git a/runtime/vm/classsupport.c b/runtime/vm/classsupport.c index 1f99a502ccc..513f7d1b19c 100644 --- a/runtime/vm/classsupport.c +++ b/runtime/vm/classsupport.c @@ -206,12 +206,12 @@ internalCreateArrayClass(J9VMThread* vmThread, J9ROMArrayClass* romClass, J9Clas BOOLEAN elementInitSuccess = TRUE; #if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) - /* When creating an array of valuetype elements, the array elements are initialized to the defaultValue of the - * element type. As a result the element type must be fully initialized (if its a valuetype) before creating an + /* When creating an array of implicitly constructible valuetype elements, the array elements are initialized to + * the defaultValue of the element type. As a result the element type must be fully initialized before creating an * instance of the array. Element class init must be done before the arrayClass is created so that in the case * of an init failure the arrayClass is not temporarily exposed. */ - if (J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(elementClass)) { + if (J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(elementClass)) { UDATA initStatus = elementClass->initializeStatus; if ((J9ClassInitSucceeded != initStatus) && ((UDATA)vmThread != initStatus)) { initializeClass(vmThread, elementClass); diff --git a/runtime/vm/createramclass.cpp b/runtime/vm/createramclass.cpp index a72ea536024..f528336d9dd 100644 --- a/runtime/vm/createramclass.cpp +++ b/runtime/vm/createramclass.cpp @@ -197,6 +197,22 @@ static void initializeClassLinks(J9Class *ramClass, J9Class *superclass, J9Memor #define MAGIC_ACCESSOR_IMPL "jdk/internal/reflect/MagicAccessorImpl" #endif /* JAVA_SPEC_VERSION == 8 */ +static J9Class * +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) +createdArrayClass(J9Class *elementClass, UDATA valueTypeFlags) { +#else /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ +createdArrayClass(J9Class *elementClass) { +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + if (J9_ARE_ALL_BITS_SET(valueTypeFlags, J9ClassArrayIsNullRestricted)) { + return elementClass->nullRestrictedArrayClass; + } else +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + { + return elementClass->arrayClass; + } +} + /** * Mark all of the interfaces supported by this class, including all interfaces * inherited by superinterfaces. Unmark all interfaces which are inherited from @@ -2093,8 +2109,21 @@ checkFlattenableFieldValueClasses(J9VMThread *currentThread, J9ClassLoader *clas static J9Class* internalCreateRAMClassDropAndReturn(J9VMThread *vmThread, J9ROMClass *romClass, J9CreateRAMClassState *state) { - /* pop protectionDomain */ - DROP_OBJECT_IN_SPECIAL_FRAME(vmThread); +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + /* This should only done once per ROM class. If two array ram + * classes are being created skip the first one. state->ramClass + * will hold the array class at this point. + */ + if (FALSE == ((NULL != state->ramClass) && + J9CLASS_IS_ARRAY(state->ramClass) && + J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(((J9ArrayClass*)state->ramClass)->componentType) && + J9_ARE_NO_BITS_SET(state->valueTypeFlags, J9ClassArrayIsNullRestricted) + )) +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + { + /* pop protectionDomain */ + DROP_OBJECT_IN_SPECIAL_FRAME(vmThread); + } Trc_VM_CreateRAMClassFromROMClass_Exit(vmThread, state->ramClass, romClass); @@ -2128,7 +2157,18 @@ internalCreateRAMClassDone(J9VMThread *vmThread, J9ClassLoader *classLoader, J9C BOOLEAN fastHCR = (0 != (options & J9_FINDCLASS_FLAG_FAST_HCR)); if (!hotswapping) { - popFromClassLoadingStack(vmThread); +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + /* This should only done once per ROM class. If two array ram + * classes are being created skip the first one. + */ + if (FALSE == (J9ROMCLASS_IS_ARRAY(romClass) && + J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(elementClass) && + J9_ARE_NO_BITS_SET(state->valueTypeFlags, J9ClassArrayIsNullRestricted) + )) +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + { + popFromClassLoadingStack(vmThread); + } } if (state->ramClass != NULL) { @@ -2175,7 +2215,11 @@ internalCreateRAMClassDone(J9VMThread *vmThread, J9ClassLoader *classLoader, J9C if (elementClass == NULL) { alreadyLoadedClass = hashClassTableAt(classLoader, J9UTF8_DATA(className), J9UTF8_LENGTH(className)); } else { - alreadyLoadedClass = elementClass->arrayClass; + alreadyLoadedClass = createdArrayClass(elementClass +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + , state->valueTypeFlags +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + ); } if (alreadyLoadedClass != NULL) { /* We are discarding this class */ @@ -2374,7 +2418,11 @@ internalCreateRAMClassDone(J9VMThread *vmThread, J9ClassLoader *classLoader, J9C if (elementClass == NULL) { alreadyLoadedClass = hashClassTableAt(classLoader, J9UTF8_DATA(className), J9UTF8_LENGTH(className)); } else { - alreadyLoadedClass = elementClass->arrayClass; + alreadyLoadedClass = createdArrayClass(elementClass +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + , state->valueTypeFlags +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + ); } if (alreadyLoadedClass != NULL) { goto alreadyLoaded; @@ -2388,7 +2436,14 @@ internalCreateRAMClassDone(J9VMThread *vmThread, J9ClassLoader *classLoader, J9C } } else { if (J9ROMCLASS_IS_ARRAY(romClass)) { - ((J9ArrayClass *)elementClass)->arrayClass = state->ramClass; +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + if (J9_ARE_ALL_BITS_SET(state->valueTypeFlags, J9ClassArrayIsNullRestricted)) { + elementClass->nullRestrictedArrayClass = state->ramClass; + } else +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + { + ((J9ArrayClass *)elementClass)->arrayClass = state->ramClass; + } /* Assigning into the arrayClass field creates an implicit reference to the class from its class loader */ javaVM->memoryManagerFunctions->j9gc_objaccess_postStoreClassToClassLoader(vmThread, classLoader, state->ramClass); } @@ -2844,7 +2899,11 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas if (elementClass == NULL) { ramClass = hashClassTableAt(classLoader, J9UTF8_DATA(className), J9UTF8_LENGTH(className)); } else { - ramClass = elementClass->arrayClass; + ramClass = createdArrayClass(elementClass, +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + *valueTypeFlags +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + ); } state->ramClass = ramClass; @@ -3189,7 +3248,7 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas * + J9ClassAllowsNonAtomicCreation * * + J9ClassNeedToPruneMemberNames - * + Unused + * + J9ClassArrayIsNullRestricted * + Unused * + Unused * @@ -3419,14 +3478,7 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas arity = elementArrayClass->arity + 1; leafComponentType = elementArrayClass->leafComponentType; } else { - U_32 arrayFlags = J9ClassLargestAlignmentConstraintReference | J9ClassLargestAlignmentConstraintDouble; - - if (J9_ARE_ALL_BITS_SET(javaVM->extendedRuntimeFlags2, J9_EXTENDED_RUNTIME2_ENABLE_VT_ARRAY_FLATTENING)) { - arrayFlags |= J9ClassIsFlattened; - } - - arity = 1; - leafComponentType = elementClass; +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) /* For arrays of valueType elements (where componentType is a valuetype), the arrays themselves are not * valuetypes but they should inherit the layout characteristics (ie. flattenable, etc.) * of the valuetype elements. A 2D (or more) array of valuetype elements (where leafComponentType is a Valuetype but @@ -3434,9 +3486,23 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas * properties from the leafComponentType. A 2D array is an array of references so it can never be flattened, however, its * elements may be flattened arrays. */ + U_32 arrayFlags = J9ClassLargestAlignmentConstraintReference | J9ClassLargestAlignmentConstraintDouble; + if (J9_ARE_ALL_BITS_SET(*valueTypeFlags, J9ClassArrayIsNullRestricted) + && J9_ARE_ALL_BITS_SET(javaVM->extendedRuntimeFlags2, J9_EXTENDED_RUNTIME2_ENABLE_VT_ARRAY_FLATTENING) + ) { + arrayFlags |= J9ClassIsFlattened; + } ramArrayClass->classFlags |= (elementClass->classFlags & arrayFlags); +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + arity = 1; + leafComponentType = elementClass; } ramArrayClass->classFlags |= J9ClassHasIdentity; +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + if (J9_ARE_ALL_BITS_SET(*valueTypeFlags, J9ClassArrayIsNullRestricted)) { + ramArrayClass->classFlags |= J9ClassArrayIsNullRestricted; + } +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ ramArrayClass->leafComponentType = leafComponentType; ramArrayClass->arity = arity; ramArrayClass->componentType = elementClass; @@ -3458,7 +3524,7 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas J9ARRAYCLASS_SET_STRIDE(ramClass, J9_VALUETYPE_FLATTENED_SIZE(elementClass)); } } else { - if (J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(elementClass)) { + if (J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(elementClass)) { ramArrayClass->classFlags |= J9ClassContainsUnflattenedFlattenables; } J9ARRAYCLASS_SET_STRIDE(ramClass, (((UDATA) 1) << (((J9ROMArrayClass*)romClass)->arrayShape & 0x0000FFFF))); @@ -3664,9 +3730,24 @@ internalCreateRAMClassFromROMClass(J9VMThread *vmThread, J9ClassLoader *classLoa result = internalCreateRAMClassFromROMClassImpl(vmThread, classLoader, romClass, options, elementClass, methodRemapArray, entryIndex, locationType, classBeingRedefined, superclass, &state, hostClassLoader, hostClass, module, flattenedClassCache, &valueTypeFlags); - if (flattenedClassCache != (J9FlattenedClassCache *) flattenedClassCacheBuffer) { - j9mem_free_memory(flattenedClassCache); + if ((NULL != result) && J9ROMCLASS_IS_ARRAY(romClass) && J9_IS_J9CLASS_ALLOW_DEFAULT_VALUE(elementClass)) { + /* Field instances of implicitly constructible fields may be marked as null-restricted. + * Create a nullRestrictedArrayClass that can be used later to create them. + */ + J9Class *nrArrayClassResult; + valueTypeFlags |= J9ClassArrayIsNullRestricted; + nrArrayClassResult = internalCreateRAMClassFromROMClassImpl(vmThread, classLoader, romClass, options, elementClass, + methodRemapArray, entryIndex, locationType, classBeingRedefined, superclass, &state, hostClassLoader, hostClass, module, flattenedClassCache, &valueTypeFlags); + + if (NULL != nrArrayClassResult) { + ((J9ArrayClass*)result)->companionArray = nrArrayClassResult; + ((J9ArrayClass*)nrArrayClassResult)->companionArray = result; } + } + + if (flattenedClassCache != (J9FlattenedClassCache *) flattenedClassCacheBuffer) { + j9mem_free_memory(flattenedClassCache); + } #else result = internalCreateRAMClassFromROMClassImpl(vmThread, classLoader, romClass, options, elementClass, methodRemapArray, entryIndex, locationType, classBeingRedefined, superclass, &state, hostClassLoader, hostClass, module); diff --git a/test/functional/Valhalla/playlist.xml b/test/functional/Valhalla/playlist.xml index e6acf1c8c48..516eca83194 100644 --- a/test/functional/Valhalla/playlist.xml +++ b/test/functional/Valhalla/playlist.xml @@ -98,11 +98,6 @@ ValueTypeArrayTests - - - https://github.com/eclipse-openj9/openj9/issues/19460 - - -Xgcpolicy:optthruput -Xgcpolicy:optthruput -XX:ValueTypeFlatteningThreshold=99999 -XX:-EnableArrayFlattening @@ -140,11 +135,6 @@ ValueTypeArrayTestsJIT - - - https://github.com/eclipse-openj9/openj9/issues/19460 - - -Xjit:count=0 -Xjit:count=1,disableAsyncCompilation -Xgcpolicy:optthruput @@ -340,11 +330,6 @@ ValueTypeSystemArraycopyTests - - - https://github.com/eclipse-openj9/openj9/issues/19460 - - -Xint -Xint -Xgcpolicy:optthruput -XX:ValueTypeFlatteningThreshold=99999 diff --git a/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeArrayTests.java b/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeArrayTests.java index 86849f0a4c9..764d820624a 100644 --- a/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeArrayTests.java +++ b/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeArrayTests.java @@ -23,8 +23,13 @@ import org.testng.Assert; import static org.testng.Assert.*; + +import java.lang.reflect.Array; + import org.testng.annotations.Test; +import jdk.internal.value.CheckedType; +import jdk.internal.value.NormalCheckedType; import jdk.internal.value.NullRestrictedCheckedType; import jdk.internal.value.ValueClass; import jdk.internal.vm.annotation.ImplicitlyConstructible; @@ -228,7 +233,7 @@ static void assignDispatch(Object[] arr, int idx, Object src, int arrKind, int s new Class[] {null, null, null, null, null}, // All values can be assigned to Object[] new Class[] {null, ASE, null, null, null}, // ASE for SomeIface[] = Object new Class[] {null, ASE, ASE, null, ASE}, // ASE for PointV[] = PointPV, SomeIface - new Class[] {NPE, ASE, ASE, ASE, null}, // NPE for PointPV[] = null; ASE for PointPV[] = PointV + new Class[] {ASE, ASE, ASE, ASE, null}, // ASE for PointPV[] = null; ASE for PointPV[] = PointV }; /** @@ -328,7 +333,8 @@ static void runTest(Object[] arr, Object sourceVal, int staticArrayKind, int sta */ @Test(priority=1,invocationCount=2) static public void testValueTypeArrayAssignments() throws Throwable { - Object[][] testArrays = new Object[][] {new Object[2], new SomeIface[2], new PointV[2], new PointPV[2]}; + Object[][] testArrays = new Object[][] {new Object[2], new SomeIface[2], new PointV[2], + ValueClass.newArrayInstance(NullRestrictedCheckedType.of(PointPV.class), 2)}; int[] kinds = {OBJ_TYPE, IFACE_TYPE, VAL_TYPE, PRIM_TYPE}; Object[] vals = new Object[] {null, bogusIfaceObj, new PointV(1.0, 2.0), new PointPV(3.0, 4.0)}; @@ -753,11 +759,11 @@ static public void testStoreNullToNullRestrictedArrayElement1() throws Throwable try { arrayElementStoreNull(dstData, ARRAY_LENGTH/2); - } catch (NullPointerException npe) { + } catch (ArrayStoreException ase) { return; /* pass */ } - Assert.fail("Expect a NullPointerException. No exception or wrong kind of exception thrown"); + Assert.fail("Expect an ArrayStoreException. No exception or wrong kind of exception thrown"); } @Test(priority=1,invocationCount=2) @@ -769,10 +775,29 @@ static public void testStoreNullToNullRestrictedArrayElement2() throws Throwable try { arrayElementStore(dstData, ARRAY_LENGTH/2, obj); - } catch (NullPointerException npe) { + } catch (ArrayStoreException ase) { return; /* pass */ } - Assert.fail("Expect a NullPointerException. No exception or wrong kind of exception thrown"); + Assert.fail("Expect an ArrayStoreException. No exception or wrong kind of exception thrown"); + } + + @ImplicitlyConstructible + public static value class EmptyNullRestricted { + } + + /* This test passes with Xint, disable until all cases are passing. */ + /* Test JVM_IsNullRestrictedArray which is called by ValueClass.componentCheckedType */ + @Test + static public void testJVMIsNullRestrictedArray() { + EmptyNullRestricted[] nrArray = (EmptyNullRestricted[])ValueClass.newArrayInstance( + NullRestrictedCheckedType.of(EmptyNullRestricted.class), 4); + CheckedType nrType = ValueClass.componentCheckedType(nrArray); + assertTrue(nrType instanceof NullRestrictedCheckedType); + + EmptyNullRestricted[] normalArray = (EmptyNullRestricted[])ValueClass.newArrayInstance( + NormalCheckedType.of(EmptyNullRestricted.class), 4); + CheckedType normalType = ValueClass.componentCheckedType(normalArray); + assertTrue(normalType instanceof NormalCheckedType); } } diff --git a/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeTests.java b/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeTests.java index 7e8b125200b..943ed42e8fe 100644 --- a/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeTests.java +++ b/test/functional/Valhalla/src_qtypes/org/openj9/test/lworld/ValueTypeTests.java @@ -572,22 +572,6 @@ static public void testDefaultValueWithNonValueType() throws Throwable { } catch (IncompatibleClassChangeError e) {} } - @Test(priority=4, invocationCount=2) - static public void testNullWritesOnNonNullableArrays() throws Throwable { - Object arrayObject = Array.newInstance(point2DClass, 3); - try { - Array.set(arrayObject, 1, null); - Assert.fail("Should throw NPE. Cant write null to arrays of valuetypes"); - } catch(NullPointerException e) {} - - Object arrayObject2 = Array.newInstance(String.class, 3); - try { - Array.set(arrayObject2, 1, null); - } catch(NullPointerException e) { - Assert.fail("Should not throw NPE. Can write null to arrays of identity types"); - } - } - @Test(priority=2, invocationCount=2) static public void testBasicACMPTestOnIdentityTypes() throws Throwable {