--- old/src/hotspot/share/classfile/javaClasses.cpp 2019-12-19 10:25:43.000000000 -0500 +++ new/src/hotspot/share/classfile/javaClasses.cpp 2019-12-19 10:25:43.000000000 -0500 @@ -4394,6 +4394,11 @@ int java_lang_Byte_ByteCache::_static_cache_offset; int java_lang_Boolean::_static_TRUE_offset; int java_lang_Boolean::_static_FALSE_offset; +int jdk_internal_vm_jni_SubElementSelector::_arrayElementType_offset; +int jdk_internal_vm_jni_SubElementSelector::_subElementType_offset; +int jdk_internal_vm_jni_SubElementSelector::_offset_offset; +int jdk_internal_vm_jni_SubElementSelector::_isFlattened_offset; +int jdk_internal_vm_jni_SubElementSelector::_isFlattenable_offset; @@ -4699,6 +4704,69 @@ #endif #undef BYTE_CACHE_FIELDS_DO +#define SUBELEMENT_SELECTOR_FIELDS_DO(macro) \ + macro(_arrayElementType_offset, k, "arrayElementType", class_signature, false); \ + macro(_subElementType_offset, k, "subElementType", class_signature, false); \ + macro(_offset_offset, k, "offset", int_signature, false); \ + macro(_isFlattened_offset, k, "isFlattened", bool_signature, false); \ + macro(_isFlattenable_offset, k, "isFlattenable", bool_signature, false); + +void jdk_internal_vm_jni_SubElementSelector::compute_offsets() { + InstanceKlass* k = SystemDictionary::jdk_internal_vm_jni_SubElementSelector_klass(); + SUBELEMENT_SELECTOR_FIELDS_DO(FIELD_COMPUTE_OFFSET); +} + +#if INCLUDE_CDS +void jdk_internal_vm_jni_SubElementSelector::serialize_offsets(SerializeClosure* f) { + SUBELEMENT_SELECTOR_FIELDS_DO(FIELD_SERIALIZE_OFFSET); +} +#endif +#undef SUBELEMENT_SELECTOR_FIELDS_DO + +Symbol* jdk_internal_vm_jni_SubElementSelector::symbol() { + return vmSymbols::jdk_internal_vm_jni_SubElementSelector(); +} + +oop jdk_internal_vm_jni_SubElementSelector::getArrayElementType(oop obj) { + return obj->obj_field(_arrayElementType_offset); +} + +void jdk_internal_vm_jni_SubElementSelector::setArrayElementType(oop obj, oop type) { + obj->obj_field_put(_arrayElementType_offset, type); +} + +oop jdk_internal_vm_jni_SubElementSelector::getSubElementType(oop obj) { + return obj->obj_field(_subElementType_offset); +} + +void jdk_internal_vm_jni_SubElementSelector::setSubElementType(oop obj, oop type) { + obj->obj_field_put(_subElementType_offset, type); +} + +int jdk_internal_vm_jni_SubElementSelector::getOffset(oop obj) { + return obj->int_field(_offset_offset); +} + +void jdk_internal_vm_jni_SubElementSelector::setOffset(oop obj, int offset) { + obj->int_field_put(_offset_offset, offset); +} + +bool jdk_internal_vm_jni_SubElementSelector::getIsFlattened(oop obj) { + return obj->bool_field(_isFlattened_offset); +} + +void jdk_internal_vm_jni_SubElementSelector::setIsFlattened(oop obj, bool b) { + obj->bool_field_put(_isFlattened_offset, b); +} + +bool jdk_internal_vm_jni_SubElementSelector::getIsFlattenable(oop obj) { + return obj->bool_field(_isFlattenable_offset); +} + +void jdk_internal_vm_jni_SubElementSelector::setIsFlattenable(oop obj, bool b) { + obj->bool_field_put(_isFlattenable_offset, b); +} + jbyte java_lang_Byte::value(oop obj) { jvalue v; java_lang_boxing_object::get_value(obj, &v); --- old/src/hotspot/share/classfile/javaClasses.hpp 2019-12-19 10:25:44.000000000 -0500 +++ new/src/hotspot/share/classfile/javaClasses.hpp 2019-12-19 10:25:44.000000000 -0500 @@ -83,6 +83,7 @@ f(java_lang_LiveStackFrameInfo) \ f(java_util_concurrent_locks_AbstractOwnableSynchronizer) \ f(jdk_internal_misc_UnsafeConstants) \ + f(jdk_internal_vm_jni_SubElementSelector) \ //end #define BASIC_JAVA_CLASSES_DO(f) \ @@ -1639,6 +1640,30 @@ static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; }; +class jdk_internal_vm_jni_SubElementSelector : AllStatic { + private: + static int _arrayElementType_offset; + static int _subElementType_offset; + static int _offset_offset; + static int _isFlattened_offset; + static int _isFlattenable_offset; + public: + static Symbol* symbol(); + static void compute_offsets(); + static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; + + static oop getArrayElementType(oop obj); + static void setArrayElementType(oop obj, oop type); + static oop getSubElementType(oop obj); + static void setSubElementType(oop obj, oop type); + static int getOffset(oop obj); + static void setOffset(oop obj, int offset); + static bool getIsFlattened(oop obj); + static void setIsFlattened(oop obj, bool b); + static bool getIsFlattenable(oop obj); + static void setIsFlattenable(oop obj, bool b); +}; + // Use to declare fields that need to be injected into Java classes // for the JVM to use. The name_index and signature_index are // declared in vmSymbols. The may_be_java flag is used to declare --- old/src/hotspot/share/classfile/systemDictionary.hpp 2019-12-19 10:25:45.000000000 -0500 +++ new/src/hotspot/share/classfile/systemDictionary.hpp 2019-12-19 10:25:45.000000000 -0500 @@ -219,6 +219,7 @@ /* force inline of iterators */ \ do_klass(Iterator_klass, java_util_Iterator ) \ \ + do_klass(jdk_internal_vm_jni_SubElementSelector_klass, jdk_internal_vm_jni_SubElementSelector ) \ /*end*/ class SystemDictionary : AllStatic { --- old/src/hotspot/share/classfile/vmSymbols.hpp 2019-12-19 10:25:46.000000000 -0500 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2019-12-19 10:25:46.000000000 -0500 @@ -674,6 +674,8 @@ \ template(java_lang_invoke_ValueBootstrapMethods, "java/lang/invoke/ValueBootstrapMethods") \ template(isSubstitutable_name, "isSubstitutable0") \ + \ + template(jdk_internal_vm_jni_SubElementSelector, "jdk/internal/vm/jni/SubElementSelector") \ /*end*/ // Here are all the intrinsics known to the runtime and the CI. --- old/src/hotspot/share/prims/jni.cpp 2019-12-19 10:25:48.000000000 -0500 +++ new/src/hotspot/share/prims/jni.cpp 2019-12-19 10:25:47.000000000 -0500 @@ -3599,6 +3599,192 @@ return (jsize)offset; JNI_END +JNI_ENTRY(jobject, jni_CreateSubElementSelector(JNIEnv* env, jarray array)) + JNIWrapper("jni_CreateSubElementSelector"); + + arrayOop ar = arrayOop(JNIHandles::resolve_non_null(array)); + if (!ar->is_array()) { + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Not an array"); + } + if (!ar->is_valueArray()) { + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Not a flattened array"); + } + Klass* ses_k = SystemDictionary::resolve_or_null(vmSymbols::jdk_internal_vm_jni_SubElementSelector(), + Handle(THREAD, SystemDictionary::java_system_loader()), Handle(), CHECK_NULL); + InstanceKlass* ses_ik = InstanceKlass::cast(ses_k); + ses_ik->initialize(CHECK_NULL); + Klass* elementKlass = ArrayKlass::cast(ar->klass())->element_klass(); + oop ses = ses_ik->allocate_instance(CHECK_NULL); + Handle ses_h(THREAD, ses); + jdk_internal_vm_jni_SubElementSelector::setArrayElementType(ses_h(), elementKlass->java_mirror()); + jdk_internal_vm_jni_SubElementSelector::setSubElementType(ses_h(), elementKlass->java_mirror()); + jdk_internal_vm_jni_SubElementSelector::setOffset(ses_h(), 0); + jdk_internal_vm_jni_SubElementSelector::setIsFlattened(ses_h(), true); // by definition, top element of a flattened array is flattened + jdk_internal_vm_jni_SubElementSelector::setIsFlattenable(ses_h(), true); // by definition, top element of a flattened array is flattenable + return JNIHandles::make_local(ses_h()); +JNI_END + +JNI_ENTRY(jobject, jni_GetSubElementSelector(JNIEnv* env, jobject selector, jfieldID fieldID)) + JNIWrapper("jni_GetSubElementSelector"); + + oop slct = JNIHandles::resolve_non_null(selector); + if (slct->klass()->name() != vmSymbols::jdk_internal_vm_jni_SubElementSelector()) { + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Not a SubElementSelector"); + } + jboolean isflattened = jdk_internal_vm_jni_SubElementSelector::getIsFlattened(slct); + if (!isflattened) { + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "SubElement is not flattened"); + } + oop semirror = jdk_internal_vm_jni_SubElementSelector::getSubElementType(slct); + Klass* k = java_lang_Class::as_Klass(semirror); + if (!k->is_value()) { + ResourceMark rm; + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), err_msg("%s is not an inline type", k->external_name())); + } + ValueKlass* vk = ValueKlass::cast(k); + assert(vk->is_initialized(), "If a flattened array has been created, the element klass must have been initialized"); + + int field_offset = jfieldIDWorkaround::from_instance_jfieldID(vk, fieldID); + fieldDescriptor fd; + if (!vk->find_field_from_offset(field_offset, false, &fd)) { + THROW_NULL(vmSymbols::java_lang_NoSuchFieldError()); + } + Handle arrayElementMirror(THREAD, jdk_internal_vm_jni_SubElementSelector::getArrayElementType(slct)); + // offset of the SubElement is offset of the original SubElement plus the offset of the field inside the element + int offset = fd.offset() - vk->first_field_offset() + jdk_internal_vm_jni_SubElementSelector::getOffset(slct); + InstanceKlass* sesklass = InstanceKlass::cast(JNIHandles::resolve_non_null(selector)->klass()); + oop res = sesklass->allocate_instance(CHECK_NULL); + Handle res_h(THREAD, res); + jdk_internal_vm_jni_SubElementSelector::setArrayElementType(res_h(), arrayElementMirror()); + InstanceKlass* holder = fd.field_holder(); + BasicType bt = char2type(fd.signature()->char_at(0)); + if (is_java_primitive(bt)) { + jdk_internal_vm_jni_SubElementSelector::setSubElementType(res_h(), java_lang_Class::primitive_mirror(bt)); + } else { + Klass* fieldKlass = SystemDictionary::resolve_or_fail(fd.signature(), Handle(THREAD, holder->class_loader()), + Handle(THREAD, holder->protection_domain()), true, CHECK_NULL); + jdk_internal_vm_jni_SubElementSelector::setSubElementType(res_h(),fieldKlass->java_mirror()); + } + jdk_internal_vm_jni_SubElementSelector::setOffset(res_h(), offset); + jdk_internal_vm_jni_SubElementSelector::setIsFlattened(res_h(), fd.is_flattened()); + jdk_internal_vm_jni_SubElementSelector::setIsFlattenable(res_h(), fd.is_flattenable()); + return JNIHandles::make_local(res_h()); +JNI_END + +JNI_ENTRY(jobject, jni_GetObjectSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + JNIWrapper("jni_GetObjectSubElement"); + + valueArrayOop ar = (valueArrayOop)JNIHandles::resolve_non_null(array); + oop slct = JNIHandles::resolve_non_null(selector); + ValueArrayKlass* vak = ValueArrayKlass::cast(ar->klass()); + if (jdk_internal_vm_jni_SubElementSelector::getArrayElementType(slct) != vak->element_klass()->java_mirror()) { + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Array/Selector mismatch"); + } + oop res = NULL; + if (!jdk_internal_vm_jni_SubElementSelector::getIsFlattened(slct)) { + int offset = (address)ar->base() - (address)ar + index * vak->element_byte_size() + + jdk_internal_vm_jni_SubElementSelector::getOffset(slct); + res = HeapAccess::oop_load_at(ar, offset); + } else { + ValueKlass* fieldKlass = ValueKlass::cast(java_lang_Class::as_Klass(jdk_internal_vm_jni_SubElementSelector::getSubElementType(slct))); + res = fieldKlass->allocate_instance(CHECK_NULL); + // The array might have been moved by the GC, refreshing the arrayOop + ar = (valueArrayOop)JNIHandles::resolve_non_null(array); + address addr = (address)ar->value_at_addr(index, vak->layout_helper()) + + jdk_internal_vm_jni_SubElementSelector::getOffset(slct); + fieldKlass->value_copy_payload_to_new_oop(addr, res); + } + return JNIHandles::make_local(res); +JNI_END + +JNI_ENTRY(void, jni_SetObjectSubElement(JNIEnv* env, jarray array, jobject selector, int index, jobject value)) + JNIWrapper("jni_SetObjectSubElement"); + + valueArrayOop ar = (valueArrayOop)JNIHandles::resolve_non_null(array); + oop slct = JNIHandles::resolve_non_null(selector); + ValueArrayKlass* vak = ValueArrayKlass::cast(ar->klass()); + if (jdk_internal_vm_jni_SubElementSelector::getArrayElementType(slct) != vak->element_klass()->java_mirror()) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Array/Selector mismatch"); + } + oop val = JNIHandles::resolve(value); + if (val == NULL) { + if (jdk_internal_vm_jni_SubElementSelector::getIsFlattenable(slct)) { + THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), "null cannot be stored in a flattened array"); + } + } else { + if (!val->is_a(java_lang_Class::as_Klass(jdk_internal_vm_jni_SubElementSelector::getSubElementType(slct)))) { + THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), "type mismatch"); + } + } + if (!jdk_internal_vm_jni_SubElementSelector::getIsFlattened(slct)) { + int offset = (address)ar->base() - (address)ar + index * vak->element_byte_size() + + jdk_internal_vm_jni_SubElementSelector::getOffset(slct); + HeapAccess::oop_store_at(ar, offset, JNIHandles::resolve(value)); + } else { + ValueKlass* fieldKlass = ValueKlass::cast(java_lang_Class::as_Klass(jdk_internal_vm_jni_SubElementSelector::getSubElementType(slct))); + address addr = (address)ar->value_at_addr(index, vak->layout_helper()) + + jdk_internal_vm_jni_SubElementSelector::getOffset(slct); + fieldKlass->value_copy_oop_to_payload(JNIHandles::resolve_non_null(value), addr); + } +JNI_END + +#define DEFINE_GETSUBELEMENT(ElementType,Result,ElementBasicType) \ +\ +JNI_ENTRY(ElementType, \ + jni_Get##Result##SubElement(JNIEnv *env, jarray array, jobject selector, int index)) \ + JNIWrapper("Get" XSTR(Result) "SubElement"); \ + valueArrayOop ar = (valueArrayOop)JNIHandles::resolve_non_null(array); \ + oop slct = JNIHandles::resolve_non_null(selector); \ + ValueArrayKlass* vak = ValueArrayKlass::cast(ar->klass()); \ + if (jdk_internal_vm_jni_SubElementSelector::getArrayElementType(slct) != vak->element_klass()->java_mirror()) { \ + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Array/Selector mismatch"); \ + } \ + if (jdk_internal_vm_jni_SubElementSelector::getSubElementType(slct) != java_lang_Class::primitive_mirror(ElementBasicType)) { \ + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong SubElement type"); \ + } \ + address addr = (address)ar->value_at_addr(index, vak->layout_helper()) \ + + jdk_internal_vm_jni_SubElementSelector::getOffset(slct); \ + ElementType result = *(ElementType*)addr; \ + return result; \ +JNI_END + +DEFINE_GETSUBELEMENT(jboolean, Boolean,T_BOOLEAN) +DEFINE_GETSUBELEMENT(jbyte, Byte, T_BYTE) +DEFINE_GETSUBELEMENT(jshort, Short,T_SHORT) +DEFINE_GETSUBELEMENT(jchar, Char,T_CHAR) +DEFINE_GETSUBELEMENT(jint, Int,T_INT) +DEFINE_GETSUBELEMENT(jlong, Long,T_LONG) +DEFINE_GETSUBELEMENT(jfloat, Float,T_FLOAT) +DEFINE_GETSUBELEMENT(jdouble, Double,T_DOUBLE) + +#define DEFINE_SETSUBELEMENT(ElementType,Result,ElementBasicType) \ +\ +JNI_ENTRY(void, \ + jni_Set##Result##SubElement(JNIEnv *env, jarray array, jobject selector, int index, ElementType value)) \ + JNIWrapper("Get" XSTR(Result) "SubElement"); \ + valueArrayOop ar = (valueArrayOop)JNIHandles::resolve_non_null(array); \ + oop slct = JNIHandles::resolve_non_null(selector); \ + ValueArrayKlass* vak = ValueArrayKlass::cast(ar->klass()); \ + if (jdk_internal_vm_jni_SubElementSelector::getArrayElementType(slct) != vak->element_klass()->java_mirror()) { \ + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Array/Selector mismatch"); \ + } \ + if (jdk_internal_vm_jni_SubElementSelector::getSubElementType(slct) != java_lang_Class::primitive_mirror(ElementBasicType)) { \ + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Wrong SubElement type"); \ + } \ + address addr = (address)ar->value_at_addr(index, vak->layout_helper()) \ + + jdk_internal_vm_jni_SubElementSelector::getOffset(slct); \ + *(ElementType*)addr = value; \ +JNI_END + +DEFINE_SETSUBELEMENT(jboolean, Boolean,T_BOOLEAN) +DEFINE_SETSUBELEMENT(jbyte, Byte, T_BYTE) +DEFINE_SETSUBELEMENT(jshort, Short,T_SHORT) +DEFINE_SETSUBELEMENT(jchar, Char,T_CHAR) +DEFINE_SETSUBELEMENT(jint, Int,T_INT) +DEFINE_SETSUBELEMENT(jlong, Long,T_LONG) +DEFINE_SETSUBELEMENT(jfloat, Float,T_FLOAT) +DEFINE_SETSUBELEMENT(jdouble, Double,T_DOUBLE) + // Structure containing all jni functions struct JNINativeInterface_ jni_NativeInterface = { NULL, @@ -3890,7 +4076,30 @@ jni_ReleaseFlattenedArrayElements, jni_GetFlattenedArrayElementClass, jni_GetFlattenedArrayElementSize, - jni_GetFieldOffsetInFlattenedLayout + jni_GetFieldOffsetInFlattenedLayout, + + jni_CreateSubElementSelector, + jni_GetSubElementSelector, + jni_GetObjectSubElement, + jni_SetObjectSubElement, + + jni_GetBooleanSubElement, + jni_GetByteSubElement, + jni_GetShortSubElement, + jni_GetCharSubElement, + jni_GetIntSubElement, + jni_GetLongSubElement, + jni_GetFloatSubElement, + jni_GetDoubleSubElement, + + jni_SetBooleanSubElement, + jni_SetByteSubElement, + jni_SetShortSubElement, + jni_SetCharSubElement, + jni_SetIntSubElement, + jni_SetLongSubElement, + jni_SetFloatSubElement, + jni_SetDoubleSubElement }; --- old/src/hotspot/share/prims/jniCheck.cpp 2019-12-19 10:25:49.000000000 -0500 +++ new/src/hotspot/share/prims/jniCheck.cpp 2019-12-19 10:25:48.000000000 -0500 @@ -2045,6 +2045,166 @@ return offset; JNI_END +JNI_ENTRY_CHECKED(jobject, + checked_jni_CreateSubElementSelector(JNIEnv* env, jarray array)) + functionEnter(thr); + jobject selector = UNCHECKED()->CreateSubElementSelector(env, array); + functionExit(thr); + return selector; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_GetSubElementSelector(JNIEnv* env, jobject selector, jfieldID fieldID)) + functionEnter(thr); + jobject res = UNCHECKED()->GetSubElementSelector(env, selector, fieldID); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(jobject, + checked_jni_GetObjectSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jobject res = UNCHECKED()->GetObjectSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetObjectSubElement(JNIEnv* env, jarray array, jobject selector, int index, jobject value)) + functionEnter(thr); + UNCHECKED()->SetObjectSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jboolean, + checked_jni_GetBooleanSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jboolean res = UNCHECKED()->GetBooleanSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetBooleanSubElement(JNIEnv* env, jarray array, jobject selector, int index, jboolean value)) + functionEnter(thr); + UNCHECKED()->SetBooleanSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jbyte, + checked_jni_GetByteSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jbyte res = UNCHECKED()->GetByteSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetByteSubElement(JNIEnv* env, jarray array, jobject selector, int index, jbyte value)) + functionEnter(thr); + UNCHECKED()->SetByteSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jshort, + checked_jni_GetShortSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jshort res = UNCHECKED()->GetShortSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetShortSubElement(JNIEnv* env, jarray array, jobject selector, int index, jshort value)) + functionEnter(thr); + UNCHECKED()->SetShortSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jchar, + checked_jni_GetCharSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jchar res = UNCHECKED()->GetCharSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetCharSubElement(JNIEnv* env, jarray array, jobject selector, int index, jchar value)) + functionEnter(thr); + UNCHECKED()->SetCharSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jint, + checked_jni_GetIntSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jint res = UNCHECKED()->GetIntSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetIntSubElement(JNIEnv* env, jarray array, jobject selector, int index, jint value)) + functionEnter(thr); + UNCHECKED()->SetIntSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jlong, + checked_jni_GetLongSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jlong res = UNCHECKED()->GetLongSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetLongSubElement(JNIEnv* env, jarray array, jobject selector, int index, jlong value)) + functionEnter(thr); + UNCHECKED()->SetLongSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jfloat, + checked_jni_GetFloatSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jfloat res = UNCHECKED()->GetFloatSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetFloatSubElement(JNIEnv* env, jarray array, jobject selector, int index, jfloat value)) + functionEnter(thr); + UNCHECKED()->SetFloatSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + +JNI_ENTRY_CHECKED(jdouble, + checked_jni_GetDoubleSubElement(JNIEnv* env, jarray array, jobject selector, int index)) + functionEnter(thr); + jdouble res = UNCHECKED()->GetDoubleSubElement(env, array, selector, index); + functionExit(thr); + return res; +JNI_END + +JNI_ENTRY_CHECKED(void, + checked_jni_SetDoubleSubElement(JNIEnv* env, jarray array, jobject selector, int index, jdouble value)) + functionEnter(thr); + UNCHECKED()->SetDoubleSubElement(env, array, selector, index, value); + functionExit(thr); + return; +JNI_END + /* * Structure containing all checked jni functions */ @@ -2337,7 +2497,30 @@ checked_jni_ReleaseFlattenedArrayElements, checked_jni_GetFlattenedArrayElementClass, checked_jni_GetFlattenedArrayElementSize, - checked_jni_GetFieldOffsetInFlattenedLayout + checked_jni_GetFieldOffsetInFlattenedLayout, + + checked_jni_CreateSubElementSelector, + checked_jni_GetSubElementSelector, + checked_jni_GetObjectSubElement, + checked_jni_SetObjectSubElement, + + checked_jni_GetBooleanSubElement, + checked_jni_GetByteSubElement, + checked_jni_GetShortSubElement, + checked_jni_GetCharSubElement, + checked_jni_GetIntSubElement, + checked_jni_GetLongSubElement, + checked_jni_GetFloatSubElement, + checked_jni_GetDoubleSubElement, + + checked_jni_SetBooleanSubElement, + checked_jni_SetByteSubElement, + checked_jni_SetShortSubElement, + checked_jni_SetCharSubElement, + checked_jni_SetIntSubElement, + checked_jni_SetLongSubElement, + checked_jni_SetFloatSubElement, + checked_jni_SetDoubleSubElement }; --- old/src/java.base/share/native/include/jni.h 2019-12-19 10:25:50.000000000 -0500 +++ new/src/java.base/share/native/include/jni.h 2019-12-19 10:25:49.000000000 -0500 @@ -787,6 +787,50 @@ (JNIEnv* env, jarray array); jsize (JNICALL *GetFieldOffsetInFlattenedLayout) (JNIEnv* env, jclass clazz, const char *name, const char *signature, jboolean* isFlattened); + + jobject (JNICALL *CreateSubElementSelector) + (JNIEnv* env, jarray array); + jobject (JNICALL *GetSubElementSelector) + (JNIEnv* env, jobject selector, jfieldID fieldID); + + jobject (JNICALL *GetObjectSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + void (JNICALL *SetObjectSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jobject value); + + jboolean (JNICALL *GetBooleanSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + jbyte (JNICALL *GetByteSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + jshort (JNICALL *GetShortSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + jchar (JNICALL *GetCharSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + jint (JNICALL *GetIntSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + jlong (JNICALL *GetLongSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + jfloat (JNICALL *GetFloatSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + jdouble (JNICALL *GetDoubleSubElement) + (JNIEnv* env, jarray array, jobject selector, int index); + + void (JNICALL *SetBooleanSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jboolean value); + void (JNICALL *SetByteSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jbyte value); + void (JNICALL *SetShortSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jshort value); + void (JNICALL *SetCharSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jchar value); + void (JNICALL *SetIntSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jint value); + void (JNICALL *SetLongSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jlong value); + void (JNICALL *SetFloatSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jfloat value); + void (JNICALL *SetDoubleSubElement) + (JNIEnv* env, jarray array, jobject selector, int index, jdouble value); }; /* @@ -1906,6 +1950,70 @@ return functions->GetFieldOffsetInFlattenedLayout(this, clazz, name, signature, isFlattened); } + jobject CreateSubElementSelector(jarray array) { + return functions->CreateSubElementSelector(this, array); + } + jobject GetSubElementSelector(jobject selector, jfieldID fieldID) { + return functions->GetSubElementSelector(this, selector, fieldID); + } + + jobject GetObjectSubElement(jarray array, jobject selector, int index) { + return functions->GetObjectSubElement(this, array, selector, index); + } + void SetObjectSubElement(jarray array, jobject selector, int index, jobject value) { + functions->SetObjectSubElement(this, array, selector, index, value); + } + + jboolean GetBooleanSubElement(jarray array, jobject selector, int index) { + return functions->GetBooleanSubElement(this, array, selector, index); + } + jbyte GetByteSubElement(jarray array, jobject selector, int index) { + return functions->GetByteSubElement(this, array, selector, index); + } + jshort GetShortSubElement(jarray array, jobject selector, int index) { + return functions->GetShortSubElement(this, array, selector, index); + } + jchar GetCharSubElement(jarray array, jobject selector, int index) { + return functions->GetCharSubElement(this, array, selector, index); + } + jint GetIntSubElement(jarray array, jobject selector, int index) { + return functions->GetIntSubElement(this, array, selector, index); + } + jlong GetLongSubElement(jarray array, jobject selector, int index) { + return functions->GetLongSubElement(this, array, selector, index); + } + jfloat GetFloatSubElement(jarray array, jobject selector, int index) { + return functions->GetFloatSubElement(this, array, selector, index); + } + jdouble GetDoubleSubElement(jarray array, jobject selector, int index) { + return functions->GetDoubleSubElement(this, array, selector, index); + } + + void SetBooleanSubElement(jarray array, jobject selector, int index, jboolean value) { + return functions->SetBooleanSubElement(this, array, selector, index, value); + } + void SetByteSubElement(jarray array, jobject selector, int index, jbyte value) { + return functions->SetByteSubElement(this, array, selector, index, value); + } + void SetShortSubElement(jarray array, jobject selector, int index, jshort value) { + return functions->SetShortSubElement(this, array, selector, index, value); + } + void SetCharSubElement(jarray array, jobject selector, int index, jchar value) { + return functions->SetCharSubElement(this, array, selector, index, value); + } + void SetIntSubElement(jarray array, jobject selector, int index, jint value) { + return functions->SetIntSubElement(this, array, selector, index, value); + } + void SetLongSubElement(jarray array, jobject selector, int index, jlong value) { + return functions->SetLongSubElement(this, array, selector, index, value); + } + void SetFloatSubElement(jarray array, jobject selector, int index, jfloat value) { + return functions->SetFloatSubElement(this, array, selector, index, value); + } + void SetDoubleSubElement(jarray array, jobject selector, int index, jdouble value) { + return functions->SetDoubleSubElement(this, array, selector, index, value); + } + #endif /* __cplusplus */ }; --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestJNIArrays.java 2019-12-19 10:25:51.000000000 -0500 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestJNIArrays.java 2019-12-19 10:25:51.000000000 -0500 @@ -30,16 +30,18 @@ import java.util.Comparator; import jdk.internal.misc.Unsafe; +import jdk.internal.vm.jni.SubElementSelector; /* * @test * @summary Test flattened arrays accesses through JNI - * @modules java.base/jdk.internal.misc + * @modules java.base/jdk.internal.misc java.base/jdk.internal.vm.jni * @library /testlibrary /test/lib * @requires (os.simpleArch == "x64") * @requires (os.family == "linux" | os.family == "mac") * @compile -XDallowGenericsOverValues -XDallowWithFieldOperator TestJNIArrays.java - * @run main/othervm/native/timeout=3000 -XX:ValueArrayElemMaxFlatSize=128 TestJNIArrays + * @run main/othervm/native/timeout=3000 -XX:ValueArrayElemMaxFlatSize=128 -XX:+PrintFlattenableLayouts -XX:+UseCompressedOops TestJNIArrays + * @run main/othervm/native/timeout=3000 -XX:ValueArrayElemMaxFlatSize=128 -XX:+PrintFlattenableLayouts -XX:-UseCompressedOops TestJNIArrays */ public class TestJNIArrays { @@ -127,8 +129,10 @@ } static inline class ValueWithOops { - String o = null; + String s = "bonjour"; int i = 0; + Containee c = new Containee(2.3f, (short)4); + BigValue b = new BigValue(); } public static void main(String[] args) { @@ -137,6 +141,7 @@ test.checkGetFlattenedArrayElementClass(); test.checkGetFieldOffsetInFlattenedLayout(); test.checkGetFlattenedArrayElements(); + test.checkSubElementAPI(); test.checkBehaviors(); // test.mesureInitializationTime(1024 * 1024 * 10 , 1000); // test.mesureInitializationTime2(1024 * 1024 * 10 , 1000); @@ -145,6 +150,72 @@ test.mesureInitializationTime3(1024 * 1024 * 2 , 1000); } + void checkSubElementAPI() { + Throwable e = null; + ValueWithOops[] arrayWithOops = new ValueWithOops[100]; + ValueWithOops v = new ValueWithOops(); + for (int i = 0; i < 100; i++) { + arrayWithOops[i] = v; + } + SubElementSelector selector1 = createSubElementSelector(arrayWithOops); + SubElementSelector selector2 = getSubElementSelector(selector1, ValueWithOops.class, "s", "Ljava/lang/String;"); + String s = (String) getObjectSubElement(arrayWithOops, selector2, 1); + System.out.println("s = " + s); + Asserts.assertEquals(s.equals("bonjour"), true, "Wrong string, expecting \"bonjour\", got " + s); + SubElementSelector selector3 = getSubElementSelector(selector1, ValueWithOops.class, "c", "QTestJNIArrays$Containee;"); + Containee c = (Containee) getObjectSubElement(arrayWithOops, selector3, 2); + Asserts.assertEquals(c.f, 2.3f, "Wrong float value: " + c.f); + Asserts.assertEquals(c.s, (short)4, "Wrong short value " + c.s); + setObjectSubElement(arrayWithOops, selector2, 1, "Hello"); + Asserts.assertEquals(arrayWithOops[1].s.equals("Hello"), true, "Wrong string, expecting \"Hello\", got " + s); + Integer myInteger = new Integer(345); + e = null; + try { + setObjectSubElement(arrayWithOops, selector2, 1, myInteger); + } catch(Throwable t) { + e = t; + } + Asserts.assertNotNull(e, "An exception should have been thrown"); + Asserts.assertEquals(e.getClass(), java.lang.ArrayStoreException.class, "Wrong exception type"); + c = new Containee(9.8f, (short)-3); + setObjectSubElement(arrayWithOops, selector3, 2, c); + Asserts.assertEquals(c.f, 9.8f, "Wrong float value: " + c.f); + Asserts.assertEquals(c.s, (short)-3, "Wrong short value " + c.s); + e = null; + try { + setObjectSubElement(arrayWithOops, selector3, 2, null); + } catch(Throwable t) { + e = t; + } + Asserts.assertNotNull(e, "An exception should have been thrown"); + Asserts.assertEquals(e.getClass(), java.lang.ArrayStoreException.class, "Wrong exception type"); + SubElementSelector selector4 = getSubElementSelector(selector3, TestJNIArrays.Containee.class, "s", "S"); + short s2 = getShortSubElement(arrayWithOops, selector4, 3); + Asserts.assertEquals(s2, (short)4, "Wrong short value " + s2); + setShortSubElement(arrayWithOops, selector4, 3, (short)7); + Asserts.assertEquals(arrayWithOops[3].c.s, (short)7, "Wrong short value " + arrayWithOops[3].c.s); + e = null; + try { + // should fail because selector4 designates a field with type short, not int + getIntSubElement(arrayWithOops, selector4, 3); + } catch(Throwable t) { + e = t; + } + Asserts.assertNotNull(e, "An exception should have been thrown"); + Asserts.assertEquals(e.getClass(), java.lang.IllegalArgumentException.class, "Wrong exception type"); + SubElementSelector selector5 = getSubElementSelector(selector1, ValueWithOops.class, "b", "QTestJNIArrays$BigValue;"); + e = null; + try { + // Should fail because selector5 designates a non-flattened field + SubElementSelector selector6 = getSubElementSelector(selector5, TestJNIArrays.BigValue.class, "l0", "J"); + } catch(Throwable t) { + e = t; + } + Asserts.assertNotNull(e, "An exception should have been thrown"); + Asserts.assertEquals(e.getClass(), java.lang.IllegalArgumentException.class, "Wrong exception type"); + System.gc(); + } + void checkGetFlattenedArrayElementSize() { Throwable exception = null; try { @@ -801,4 +872,14 @@ native void updateContainerArray(Object[] array, float f, short s); native void initializeLongLongLongLongArray(Object[] array, long l0, long l1, long l2, long l3); + + native SubElementSelector createSubElementSelector(Object[] array); + native SubElementSelector getSubElementSelector(SubElementSelector selector, Class klass, String name, String signature); + native Object getObjectSubElement(Object[] array, SubElementSelector selector, int index); + native void setObjectSubElement(Object[] array, SubElementSelector selector, int index, Object value); + + native short getShortSubElement(Object[] array, SubElementSelector selector, int index); + native void setShortSubElement(Object[] array, SubElementSelector selector, int index, short value); + native int getIntSubElement(Object[] array, SubElementSelector selector, int index); + native void setIntSubElement(Object[] array, SubElementSelector selector, int index, int value); } --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/libTestJNIArrays.c 2019-12-19 10:25:52.000000000 -0500 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/libTestJNIArrays.c 2019-12-19 10:25:52.000000000 -0500 @@ -244,7 +244,7 @@ JNIEXPORT void JNICALL - Java_TestJNIArrays_initializeLongLongLongLongArray(JNIEnv* env, jobject receiver, jarray array, jlong l0, jlong l1, jlong l2, jlong l3) { + Java_TestJNIArrays_initializeLongLongLongLongArray(JNIEnv* env, jobject receiver, jarray array, jlong l0, jlong l1, jlong l2, jlong l3) { int len = (*env)->GetArrayLength(env, array); jsize elm_sz = (*env)->GetFlattenedArrayElementSize(env, array); jclass clazz = (*env)->GetFlattenedArrayElementClass(env, array); @@ -264,4 +264,50 @@ (*env)->ReleaseFlattenedArrayElements(env, array, base, 0); } +JNIEXPORT jobject JNICALL +Java_TestJNIArrays_createSubElementSelector(JNIEnv* env, jobject receiver, jarray array) { + return (*env)->CreateSubElementSelector(env, array); +} + +JNIEXPORT jobject JNICALL + Java_TestJNIArrays_getSubElementSelector(JNIEnv* env, jobject receiver, jobject selector, jclass klass, jstring name, jstring signature) { + const char *name_ptr = (*env)->GetStringUTFChars(env, name, NULL); + const char *signature_ptr = (*env)->GetStringUTFChars(env, signature, NULL); + jfieldID fieldID = (*env)->GetFieldID(env, klass, name_ptr, signature_ptr); + jobject res = (*env)->GetSubElementSelector(env, selector, fieldID); + (*env)->ReleaseStringUTFChars(env, name, name_ptr); + (*env)->ReleaseStringUTFChars(env, signature, signature_ptr); + return res; +} + +JNIEXPORT jobject JNICALL +Java_TestJNIArrays_getObjectSubElement(JNIEnv* env, jobject receiver, jarray array, jobject selector, jint index) { + return (*env)->GetObjectSubElement(env, array, selector, index); +} + +JNIEXPORT void JNICALL + Java_TestJNIArrays_setObjectSubElement(JNIEnv* env, jobject receiver, jarray array, jobject selector, jint index, jobject value) { + (*env)->SetObjectSubElement(env, array, selector, index, value); +} + +JNIEXPORT jshort JNICALL +Java_TestJNIArrays_getShortSubElement(JNIEnv* env, jobject receiver, jarray array, jobject selector, jint index) { + return (*env)->GetShortSubElement(env, array, selector, index); +} + +JNIEXPORT void JNICALL + Java_TestJNIArrays_setShortSubElement(JNIEnv* env, jobject receiver, jarray array, jobject selector, jint index, short value) { + (*env)->SetShortSubElement(env, array, selector, index, value); +} + +JNIEXPORT jint JNICALL +Java_TestJNIArrays_getIntSubElement(JNIEnv* env, jobject receiver, jarray array, jobject selector, jint index) { + return (*env)->GetIntSubElement(env, array, selector, index); +} + +JNIEXPORT void JNICALL + Java_TestJNIArrays_setIntSubElement(JNIEnv* env, jobject receiver, jarray array, jobject selector, jint index, jint value) { + (*env)->SetIntSubElement(env, array, selector, index, value); +} + #endif // !defined(_WIN32) && !defined(_WIN64) --- /dev/null 2019-12-19 10:25:53.000000000 -0500 +++ new/src/java.base/share/classes/jdk/internal/vm/jni/SubElementSelector.java 2019-12-19 10:25:53.000000000 -0500 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 2019 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.vm.jni; + +public /*inline*/ class SubElementSelector { + public final Class arrayElementType = null; + public final Class subElementType = null; + public final int offset = -1; + public final boolean isFlattened = false; + public final boolean isFlattenable = false; +}