diff --git a/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/InternalDowncallHandler.java b/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/InternalDowncallHandler.java index 0b0c2d26ccd..722592dd13e 100644 --- a/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/InternalDowncallHandler.java +++ b/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/InternalDowncallHandler.java @@ -703,7 +703,8 @@ private MethodHandle getArgumentFilter(Class argTypeClass, MemoryLayout argLa /*[ENDIF] JAVA_SPEC_VERSION <= 19 */ if (argTypeClass == MemorySegment.class) { /*[IF JAVA_SPEC_VERSION >= 20]*/ - if (argLayout == ADDRESS) { + /* The address layout for pointer might come with different representations of ADDRESS. */ + if (argLayout instanceof OfAddress) { filterMH = memSegmtOfPtrToLongArgFilter; } else /*[ENDIF] JAVA_SPEC_VERSION >= 20 */ diff --git a/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/UpcallMHMetaData.java b/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/UpcallMHMetaData.java index f1b0822302c..0a25cc71a2c 100644 --- a/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/UpcallMHMetaData.java +++ b/jcl/src/java.base/share/classes/openj9/internal/foreign/abi/UpcallMHMetaData.java @@ -116,12 +116,6 @@ final class UpcallMHMetaData { * The method is shared in java (downcall) and in native (upcall) via the calling-in from the dispatcher. */ static long getNativeArgRetSegmentOfPtr(MemorySegment argRetSegmentOfPtr) { - /*[IF JAVA_SPEC_VERSION >= 20]*/ - /* Verify MemorySegment.NULL as it is introduced since JDK20. */ - if (argRetSegmentOfPtr == MemorySegment.NULL) { - throw new NullPointerException("A NULL memory segment is not allowed for pointer."); - } - /*[ENDIF] JAVA_SPEC_VERSION >= 20 */ if (!argRetSegmentOfPtr.isNative()) { throw new IllegalArgumentException("Heap segment not allowed: " + argRetSegmentOfPtr); } @@ -141,10 +135,6 @@ static long getNativeArgRetSegmentOfPtr(MemorySegment argRetSegmentOfPtr) { * The method is shared in java (downcall) and in native (upcall) via the calling-in from the dispatcher. */ static long getNativeArgRetAddrOfPtr(MemoryAddress argRetAddrOfPtr) { - if (argRetAddrOfPtr == MemoryAddress.NULL) { - throw new NullPointerException("A NULL memory address is not allowed for pointer."); - } - /*[IF JAVA_SPEC_VERSION > 17]*/ /* Validate the native address as MemoryAddress.isNative() is removed in JDK18/19. */ if (argRetAddrOfPtr.toRawLongValue() == 0) @@ -169,8 +159,7 @@ static long getNativeArgRetAddrOfPtr(MemoryAddress argRetAddrOfPtr) { static long getNativeArgRetSegment(MemorySegment argRetSegment) { /*[IF JAVA_SPEC_VERSION >= 20]*/ /* MemorySegment.NULL is introduced since JDK20+. */ - if (argRetSegment == MemorySegment.NULL) - { + if (argRetSegment == MemorySegment.NULL) { throw new NullPointerException("A NULL memory segment is not allowed for struct."); } /*[ENDIF] JAVA_SPEC_VERSION >= 20 */ diff --git a/runtime/tests/clinkerffi/CMakeLists.txt b/runtime/tests/clinkerffi/CMakeLists.txt index 0e13a80cc83..5fc0fda47a0 100644 --- a/runtime/tests/clinkerffi/CMakeLists.txt +++ b/runtime/tests/clinkerffi/CMakeLists.txt @@ -164,6 +164,7 @@ omr_add_exports(clinkerffitests add2DoubleStructs_returnStruct add2DoubleStructs_returnStructPointer add3DoubleStructs_returnStruct + validateNullAddrArgument add2BoolsWithOrByUpcallMH addBoolAndBoolFromPointerWithOrByUpcallMH addBoolAndBoolFromNativePtrWithOrByUpcallMH @@ -341,6 +342,7 @@ omr_add_exports(clinkerffitests addDoubleAndIntDoubleLongFromStructByUpcallMH return254BytesFromStructByUpcallMH return4KBytesFromStructByUpcallMH + validateReturnNullAddrByUpcallMH addIntsFromVaList addLongsFromVaList addDoublesFromVaList diff --git a/runtime/tests/clinkerffi/downcall.c b/runtime/tests/clinkerffi/downcall.c index 3aee647b14f..b18606de239 100644 --- a/runtime/tests/clinkerffi/downcall.c +++ b/runtime/tests/clinkerffi/downcall.c @@ -1990,3 +1990,20 @@ add3DoubleStructs_returnStruct(stru_Double_Double_Double arg1, stru_Double_Doubl doubleStruct.elem3 = arg1.elem3 + arg2.elem3; return doubleStruct; } + +/** + * Validate that a null pointer of struct is successfully passed + * from java to native in downcall. + * + * @param arg1 an integer + * @param arg2 a pointer to struct with two integers + * @return the value of arg1 + * + * Note: + * arg2 is a null pointer passed from java to native. + */ +int +validateNullAddrArgument(int arg1, stru_Int_Int *arg2) +{ + return arg1; +} diff --git a/runtime/tests/clinkerffi/module.xml b/runtime/tests/clinkerffi/module.xml index d3d35d82473..ddfa45466e3 100644 --- a/runtime/tests/clinkerffi/module.xml +++ b/runtime/tests/clinkerffi/module.xml @@ -155,6 +155,7 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-excepti + @@ -332,6 +333,7 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-excepti + diff --git a/runtime/tests/clinkerffi/upcall.c b/runtime/tests/clinkerffi/upcall.c index 8ad23fc7d95..007d630c126 100644 --- a/runtime/tests/clinkerffi/upcall.c +++ b/runtime/tests/clinkerffi/upcall.c @@ -2847,3 +2847,22 @@ return4KBytesFromStructByUpcallMH(stru_4K_Bytes (*upcallMH)()) { return (*upcallMH)(); } + +/** + * Validate that a null pointer is successfully returned + * from the upcall method to native. + * + * @param arg1 a pointer to the 1st struct with two ints + * @param arg2 the 2nd struct with two ints + * @param upcallMH the function pointer to the upcall method + * @return a pointer to struct with two ints (arg1) + * + * Note: + * A null pointer is returned from upcallMH(). + */ +stru_Int_Int * +validateReturnNullAddrByUpcallMH(stru_Int_Int *arg1, stru_Int_Int arg2, stru_Int_Int * (*upcallMH)(stru_Int_Int *, stru_Int_Int)) +{ + (*upcallMH)(arg1, arg2); + return arg1; +} diff --git a/test/functional/Java17andUp/src_170/org/openj9/test/jep389/downcall/InvalidDownCallTests.java b/test/functional/Java17andUp/src_170/org/openj9/test/jep389/downcall/InvalidDownCallTests.java index 8634669c4ad..f8047c3c67b 100644 --- a/test/functional/Java17andUp/src_170/org/openj9/test/jep389/downcall/InvalidDownCallTests.java +++ b/test/functional/Java17andUp/src_170/org/openj9/test/jep389/downcall/InvalidDownCallTests.java @@ -436,7 +436,6 @@ public void test_nullValueForStructArgument() throws Throwable { } } - @Test(expectedExceptions = NullPointerException.class) public void test_nullSegmentForPtrArgument() throws Throwable { GroupLayout structLayout = MemoryLayout.structLayout(C_INT.withName("elem1"), C_INT.withName("elem2")); VarHandle intHandle1 = structLayout.varHandle(int.class, PathElement.groupElement("elem1")); @@ -444,11 +443,11 @@ public void test_nullSegmentForPtrArgument() throws Throwable { MethodType mt = MethodType.methodType(int.class, int.class, MemoryAddress.class); FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, C_POINTER); - Addressable functionSymbol = nativeLibLookup.lookup("addIntAndIntsFromStructPointer").get(); + Addressable functionSymbol = nativeLibLookup.lookup("validateNullAddrArgument").get(); MethodHandle mh = clinker.downcallHandle(functionSymbol, mt, fd); int result = (int)mh.invoke(19202122, MemoryAddress.NULL); - fail("Failed to throw out NullPointerException in the case of the null memory address"); + Assert.assertEquals(result, 19202122); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "A heap address is not allowed.*") diff --git a/test/functional/Java17andUp/src_170/org/openj9/test/jep389/upcall/InvalidUpCallTests.java b/test/functional/Java17andUp/src_170/org/openj9/test/jep389/upcall/InvalidUpCallTests.java index c1479093a35..64e5427eae8 100644 --- a/test/functional/Java17andUp/src_170/org/openj9/test/jep389/upcall/InvalidUpCallTests.java +++ b/test/functional/Java17andUp/src_170/org/openj9/test/jep389/upcall/InvalidUpCallTests.java @@ -170,7 +170,6 @@ public void test_nullValueForReturnStruct() throws Throwable { } } - @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "A NULL memory address is not allowed for pointer.*") public void test_nullAddrForReturnPtr() throws Throwable { GroupLayout structLayout = MemoryLayout.structLayout(C_INT.withName("elem1"), C_INT.withName("elem2")); VarHandle intHandle1 = structLayout.varHandle(int.class, PathElement.groupElement("elem1")); @@ -178,7 +177,7 @@ public void test_nullAddrForReturnPtr() throws Throwable { MethodType mt = MethodType.methodType(MemoryAddress.class, MemoryAddress.class, MemorySegment.class, MemoryAddress.class); FunctionDescriptor fd = FunctionDescriptor.of(C_POINTER, C_POINTER, structLayout, C_POINTER); - Addressable functionSymbol = nativeLibLookup.lookup("add2IntStructs_returnStructPointerByUpcallMH").get(); + Addressable functionSymbol = nativeLibLookup.lookup("validateReturnNullAddrByUpcallMH").get(); MethodHandle mh = clinker.downcallHandle(functionSymbol, mt, fd); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -193,7 +192,9 @@ public void test_nullAddrForReturnPtr() throws Throwable { intHandle2.set(structSegmt2, 33445566); MemoryAddress resultAddr = (MemoryAddress)mh.invokeExact(structSegmt1.address(), structSegmt2, upcallFuncAddr); - fail("Failed to throw out NullPointerException in the case of the null pointer upon return"); + MemorySegment resultSegmt = resultAddr.asSegment(structLayout.byteSize(), scope); + Assert.assertEquals(intHandle1.get(resultSegmt), 11223344); + Assert.assertEquals(intHandle2.get(resultSegmt), 55667788); } } diff --git a/test/functional/Java20andUp/src/org/openj9/test/jep434/downcall/InvalidDownCallTests.java b/test/functional/Java20andUp/src/org/openj9/test/jep434/downcall/InvalidDownCallTests.java index 327d43fe87a..f9fb76e816e 100644 --- a/test/functional/Java20andUp/src/org/openj9/test/jep434/downcall/InvalidDownCallTests.java +++ b/test/functional/Java20andUp/src/org/openj9/test/jep434/downcall/InvalidDownCallTests.java @@ -119,18 +119,17 @@ public void test_nullValueForStructArgument() throws Throwable { } } - @Test(expectedExceptions = NullPointerException.class) public void test_nullSegmentForPtrArgument() throws Throwable { GroupLayout structLayout = MemoryLayout.structLayout(JAVA_INT.withName("elem1"), JAVA_INT.withName("elem2")); VarHandle intHandle1 = structLayout.varHandle(PathElement.groupElement("elem1")); VarHandle intHandle2 = structLayout.varHandle(PathElement.groupElement("elem2")); FunctionDescriptor fd = FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS); - MemorySegment functionSymbol = nativeLibLookup.find("addIntAndIntsFromStructPointer").get(); + MemorySegment functionSymbol = nativeLibLookup.find("validateNullAddrArgument").get(); MethodHandle mh = linker.downcallHandle(functionSymbol, fd); int result = (int)mh.invoke(19202122, MemorySegment.NULL); - fail("Failed to throw out NullPointerException in the case of the null segment"); + Assert.assertEquals(result, 19202122); } @Test(expectedExceptions = NullPointerException.class) diff --git a/test/functional/Java20andUp/src/org/openj9/test/jep434/upcall/InvalidUpCallTests.java b/test/functional/Java20andUp/src/org/openj9/test/jep434/upcall/InvalidUpCallTests.java index 86aed4b652e..567c430e547 100644 --- a/test/functional/Java20andUp/src/org/openj9/test/jep434/upcall/InvalidUpCallTests.java +++ b/test/functional/Java20andUp/src/org/openj9/test/jep434/upcall/InvalidUpCallTests.java @@ -159,14 +159,13 @@ public void test_nullValueForReturnStruct() throws Throwable { } } - @Test(expectedExceptions = NullPointerException.class) public void test_nullSegmentForReturnPtr() throws Throwable { GroupLayout structLayout = MemoryLayout.structLayout(JAVA_INT.withName("elem1"), JAVA_INT.withName("elem2")); VarHandle intHandle1 = structLayout.varHandle(PathElement.groupElement("elem1")); VarHandle intHandle2 = structLayout.varHandle(PathElement.groupElement("elem2")); FunctionDescriptor fd = FunctionDescriptor.of(ADDRESS, ADDRESS, structLayout, ADDRESS); - MemorySegment functionSymbol = nativeLibLookup.find("add2IntStructs_returnStructPointerByUpcallMH").get(); + MemorySegment functionSymbol = nativeLibLookup.find("validateReturnNullAddrByUpcallMH").get(); MethodHandle mh = linker.downcallHandle(functionSymbol, fd); try (Arena arena = Arena.openConfined()) { @@ -181,7 +180,9 @@ public void test_nullSegmentForReturnPtr() throws Throwable { intHandle2.set(structSegmt2, 33445566); MemorySegment resultAddr = (MemorySegment)mh.invoke(structSegmt1, structSegmt2, upcallFuncAddr); - fail("Failed to throw out NullPointerException in the case of the null segment upon return"); + MemorySegment resultSegmt = MemorySegment.ofAddress(resultAddr.address(), structLayout.byteSize(), arena.scope()); + Assert.assertEquals(resultSegmt.get(JAVA_INT, 0), 11223344); + Assert.assertEquals(resultSegmt.get(JAVA_INT, 4), 55667788); } } diff --git a/test/functional/Java21andUp/src/org/openj9/test/jep442/downcall/InvalidDownCallTests.java b/test/functional/Java21andUp/src/org/openj9/test/jep442/downcall/InvalidDownCallTests.java index 29ebcf4abbb..ab8df6e7344 100644 --- a/test/functional/Java21andUp/src/org/openj9/test/jep442/downcall/InvalidDownCallTests.java +++ b/test/functional/Java21andUp/src/org/openj9/test/jep442/downcall/InvalidDownCallTests.java @@ -119,18 +119,17 @@ public void test_nullValueForStructArgument() throws Throwable { } } - @Test(expectedExceptions = NullPointerException.class) public void test_nullSegmentForPtrArgument() throws Throwable { GroupLayout structLayout = MemoryLayout.structLayout(JAVA_INT.withName("elem1"), JAVA_INT.withName("elem2")); VarHandle intHandle1 = structLayout.varHandle(PathElement.groupElement("elem1")); VarHandle intHandle2 = structLayout.varHandle(PathElement.groupElement("elem2")); FunctionDescriptor fd = FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS); - MemorySegment functionSymbol = nativeLibLookup.find("addIntAndIntsFromStructPointer").get(); + MemorySegment functionSymbol = nativeLibLookup.find("validateNullAddrArgument").get(); MethodHandle mh = linker.downcallHandle(functionSymbol, fd); int result = (int)mh.invoke(19202122, MemorySegment.NULL); - fail("Failed to throw out NullPointerException in the case of the null segment"); + Assert.assertEquals(result, 19202122); } @Test(expectedExceptions = NullPointerException.class) diff --git a/test/functional/Java21andUp/src/org/openj9/test/jep442/upcall/InvalidUpCallTests.java b/test/functional/Java21andUp/src/org/openj9/test/jep442/upcall/InvalidUpCallTests.java index 080e013b938..090a76ba1d3 100644 --- a/test/functional/Java21andUp/src/org/openj9/test/jep442/upcall/InvalidUpCallTests.java +++ b/test/functional/Java21andUp/src/org/openj9/test/jep442/upcall/InvalidUpCallTests.java @@ -159,14 +159,13 @@ public void test_nullValueForReturnStruct() throws Throwable { } } - @Test(expectedExceptions = NullPointerException.class) public void test_nullSegmentForReturnPtr() throws Throwable { GroupLayout structLayout = MemoryLayout.structLayout(JAVA_INT.withName("elem1"), JAVA_INT.withName("elem2")); VarHandle intHandle1 = structLayout.varHandle(PathElement.groupElement("elem1")); VarHandle intHandle2 = structLayout.varHandle(PathElement.groupElement("elem2")); FunctionDescriptor fd = FunctionDescriptor.of(ADDRESS, ADDRESS, structLayout, ADDRESS); - MemorySegment functionSymbol = nativeLibLookup.find("add2IntStructs_returnStructPointerByUpcallMH").get(); + MemorySegment functionSymbol = nativeLibLookup.find("validateReturnNullAddrByUpcallMH").get(); MethodHandle mh = linker.downcallHandle(functionSymbol, fd); try (Arena arena = Arena.openConfined()) { @@ -181,7 +180,9 @@ public void test_nullSegmentForReturnPtr() throws Throwable { intHandle2.set(structSegmt2, 33445566); MemorySegment resultAddr = (MemorySegment)mh.invoke(structSegmt1, structSegmt2, upcallFuncAddr); - fail("Failed to throw out NullPointerException in the case of the null segment upon return"); + MemorySegment resultSegmt = MemorySegment.ofAddress(resultAddr.address(), structLayout.byteSize(), arena.scope()); + Assert.assertEquals(resultSegmt.get(JAVA_INT, 0), 11223344); + Assert.assertEquals(resultSegmt.get(JAVA_INT, 4), 55667788); } }