From cbccc4c8172797ea2f1b7c301d00add3f517546d Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Thu, 27 Apr 2023 09:00:58 +0000 Subject: [PATCH] 8304265: Implementation of Foreign Function and Memory API (Third Preview) Co-authored-by: Maurizio Cimadamore Co-authored-by: Jorn Vernee Co-authored-by: Paul Sandoz Co-authored-by: Feilong Jiang Co-authored-by: Per Minborg Reviewed-by: erikj, jvernee, vlivanov, psandoz --- make/autoconf/configure.ac | 4 + make/autoconf/jdk-options.m4 | 19 + make/autoconf/libraries.m4 | 2 +- make/autoconf/spec.gmk.in | 3 + make/conf/jib-profiles.js | 46 +- make/data/hotspot-symbols/symbols-shared | 3 +- make/devkit/createLibffiBundle.sh | 111 ++ make/modules/java.base/Lib.gmk | 17 +- .../cpu/aarch64/downcallLinker_aarch64.cpp | 154 +-- .../cpu/aarch64/foreignGlobals_aarch64.cpp | 6 +- src/hotspot/cpu/arm/downcallLinker_arm.cpp | 5 +- src/hotspot/cpu/arm/foreignGlobals_arm.cpp | 6 +- src/hotspot/cpu/ppc/downcallLinker_ppc.cpp | 5 +- src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp | 6 +- .../cpu/riscv/downcallLinker_riscv.cpp | 150 +-- .../cpu/riscv/foreignGlobals_riscv.cpp | 6 +- src/hotspot/cpu/s390/downcallLinker_s390.cpp | 5 +- src/hotspot/cpu/s390/foreignGlobals_s390.cpp | 6 +- src/hotspot/cpu/x86/downcallLinker_x86_32.cpp | 5 +- src/hotspot/cpu/x86/downcallLinker_x86_64.cpp | 170 +-- src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp | 6 +- src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp | 6 +- src/hotspot/cpu/zero/downcallLinker_zero.cpp | 5 +- src/hotspot/cpu/zero/foreignGlobals_zero.cpp | 6 +- .../cpu/zero/globalDefinitions_zero.hpp | 4 +- src/hotspot/share/include/jvm.h | 3 + src/hotspot/share/prims/downcallLinker.cpp | 6 +- src/hotspot/share/prims/downcallLinker.hpp | 5 +- src/hotspot/share/prims/foreignGlobals.hpp | 4 +- src/hotspot/share/prims/jvm.cpp | 5 + src/hotspot/share/prims/nativeEntryPoint.cpp | 10 +- src/hotspot/share/prims/upcallLinker.cpp | 1 + .../java/lang/foreign/AddressLayout.java | 130 +++ .../classes/java/lang/foreign/Arena.java | 257 ++++- .../java/lang/foreign/FunctionDescriptor.java | 4 +- .../java/lang/foreign/GroupLayout.java | 11 +- .../classes/java/lang/foreign/Linker.java | 587 +++++++--- .../java/lang/foreign/MemoryLayout.java | 161 +-- .../java/lang/foreign/MemorySegment.java | 1008 +++++++++-------- .../java/lang/foreign/PaddingLayout.java | 7 + .../java/lang/foreign/SegmentAllocator.java | 38 +- .../java/lang/foreign/SegmentScope.java | 132 --- .../java/lang/foreign/SequenceLayout.java | 15 +- .../java/lang/foreign/StructLayout.java | 15 +- .../java/lang/foreign/SymbolLookup.java | 83 +- .../java/lang/foreign/UnionLayout.java | 9 +- .../classes/java/lang/foreign/VaList.java | 352 ------ .../java/lang/foreign/ValueLayout.java | 164 ++- .../java/lang/foreign/package-info.java | 160 +-- .../share/classes/java/nio/Buffer.java | 4 +- .../java/nio/channels/FileChannel.java | 24 +- .../foreign/AbstractMemorySegmentImpl.java | 101 +- .../classes/jdk/internal/foreign/CABI.java | 84 +- .../foreign/HeapMemorySegmentImpl.java | 98 +- .../jdk/internal/foreign/LayoutPath.java | 110 +- .../foreign/MappedMemorySegmentImpl.java | 54 +- .../internal/foreign/MemorySessionImpl.java | 39 +- .../foreign/NativeMemorySegmentImpl.java | 43 +- .../internal/foreign/SlicingAllocator.java | 6 +- .../jdk/internal/foreign/SystemLookup.java | 42 +- .../classes/jdk/internal/foreign/Utils.java | 94 +- .../internal/foreign/abi/AbstractLinker.java | 22 +- .../jdk/internal/foreign/abi/Binding.java | 186 +-- .../foreign/abi/BindingInterpreter.java | 11 +- .../foreign/abi/BindingSpecializer.java | 61 +- .../internal/foreign/abi/CallingSequence.java | 6 +- .../internal/foreign/abi/CapturableState.java | 31 +- .../internal/foreign/abi/DowncallLinker.java | 25 +- .../internal/foreign/abi/LinkerOptions.java | 56 +- .../foreign/abi/NativeEntryPoint.java | 17 +- .../jdk/internal/foreign/abi/SharedUtils.java | 175 ++- .../internal/foreign/abi/UpcallLinker.java | 8 +- .../jdk/internal/foreign/abi/UpcallStubs.java | 10 +- .../abi/aarch64/AArch64Architecture.java | 5 +- .../foreign/abi/aarch64/CallArranger.java | 31 +- .../abi/aarch64/linux/LinuxAArch64Linker.java | 21 +- .../abi/aarch64/linux/LinuxAArch64VaList.java | 568 ---------- .../abi/aarch64/macos/MacOsAArch64Linker.java | 21 +- .../abi/aarch64/macos/MacOsAArch64VaList.java | 257 ----- .../aarch64/windows/WindowsAArch64Linker.java | 22 +- .../aarch64/windows/WindowsAArch64VaList.java | 261 ----- .../internal/foreign/abi/fallback/FFIABI.java | 42 + .../foreign/abi/fallback/FFIStatus.java | 52 + .../foreign/abi/fallback/FFIType.java | 146 +++ .../foreign/abi/fallback/FallbackLinker.java | 272 +++++ .../foreign/abi/fallback/LibFallback.java | 219 ++++ .../abi/riscv64/RISCV64Architecture.java | 5 +- .../linux/LinuxRISCV64CallArranger.java | 13 +- .../abi/riscv64/linux/LinuxRISCV64Linker.java | 22 +- .../abi/riscv64/linux/LinuxRISCV64VaList.java | 302 ----- .../foreign/abi/x64/X86_64Architecture.java | 9 +- .../foreign/abi/x64/sysv/CallArranger.java | 20 +- .../foreign/abi/x64/sysv/SysVVaList.java | 479 -------- .../foreign/abi/x64/sysv/SysVx64Linker.java | 21 +- .../foreign/abi/x64/windows/CallArranger.java | 13 +- .../foreign/abi/x64/windows/WinVaList.java | 246 ---- .../abi/x64/windows/Windowsx64Linker.java | 22 +- .../foreign/layout/AbstractGroupLayout.java | 59 +- .../foreign/layout/AbstractLayout.java | 68 +- .../foreign/layout/MemoryLayoutUtil.java | 16 +- .../foreign/layout/PaddingLayoutImpl.java | 18 +- .../foreign/layout/SequenceLayoutImpl.java | 28 +- .../foreign/layout/StructLayoutImpl.java | 21 +- .../foreign/layout/UnionLayoutImpl.java | 18 +- .../internal/foreign/layout/ValueLayouts.java | 315 +++--- .../jdk/internal/javac/PreviewFeature.java | 2 +- .../jdk/internal/vm/ForeignLinkerSupport.java | 44 + .../classes/sun/nio/ch/FileChannelImpl.java | 12 +- .../native/libfallbackLinker/fallbackLinker.c | 203 ++++ .../native/libjava/ForeignLinkerSupport.c | 34 + test/jdk/TEST.groups | 1 + test/jdk/java/foreign/LibraryLookupTest.java | 40 +- .../MemoryLayoutPrincipalTotalityTest.java | 6 +- .../MemoryLayoutTypeRetentionTest.java | 61 +- test/jdk/java/foreign/NativeTestHelper.java | 11 +- .../java/foreign/SafeFunctionAccessTest.java | 46 +- test/jdk/java/foreign/StdLibTest.java | 87 +- .../jdk/java/foreign/TestAdaptVarHandles.java | 40 +- .../java/foreign/TestAddressDereference.java | 201 ++++ test/jdk/java/foreign/TestArrays.java | 23 +- test/jdk/java/foreign/TestByteBuffer.java | 229 ++-- .../foreign/TestClassLoaderFindNative.java | 9 +- .../jdk/java/foreign/TestDereferencePath.java | 141 +++ test/jdk/java/foreign/TestDowncallScope.java | 12 +- test/jdk/java/foreign/TestDowncallStack.java | 4 +- .../java/foreign/TestFunctionDescriptor.java | 21 +- test/jdk/java/foreign/TestHandshake.java | 30 +- test/jdk/java/foreign/TestHeapAlignment.java | 9 +- test/jdk/java/foreign/TestIllegalLink.java | 48 +- test/jdk/java/foreign/TestIntrinsics.java | 4 +- .../java/foreign/TestLargeSegmentCopy.java | 6 +- test/jdk/java/foreign/TestLayoutPaths.java | 191 +--- test/jdk/java/foreign/TestLayouts.java | 199 +++- test/jdk/java/foreign/TestMemoryAccess.java | 21 +- .../foreign/TestMemoryAccessInstance.java | 39 +- .../jdk/java/foreign/TestMemoryAlignment.java | 39 +- test/jdk/java/foreign/TestMemorySession.java | 171 +-- test/jdk/java/foreign/TestMismatch.java | 25 +- test/jdk/java/foreign/TestNULLAddress.java | 6 +- test/jdk/java/foreign/TestNative.java | 39 +- test/jdk/java/foreign/TestNulls.java | 60 +- .../java/foreign/TestScopedOperations.java | 109 +- .../java/foreign/TestSegmentAllocators.java | 54 +- test/jdk/java/foreign/TestSegmentCopy.java | 6 +- test/jdk/java/foreign/TestSegmentOffset.java | 6 +- test/jdk/java/foreign/TestSegmentOverlap.java | 10 +- test/jdk/java/foreign/TestSegments.java | 140 ++- test/jdk/java/foreign/TestSharedAccess.java | 18 +- test/jdk/java/foreign/TestSlices.java | 117 +- test/jdk/java/foreign/TestSpliterator.java | 44 +- test/jdk/java/foreign/TestStringEncoding.java | 4 +- test/jdk/java/foreign/TestTypeAccess.java | 18 +- .../java/foreign/TestUnsupportedLinker.java | 22 +- test/jdk/java/foreign/TestUpcallAsync.java | 6 +- .../jdk/java/foreign/TestUpcallException.java | 86 +- .../jdk/java/foreign/TestUpcallHighArity.java | 4 +- test/jdk/java/foreign/TestUpcallScope.java | 4 +- test/jdk/java/foreign/TestUpcallStack.java | 4 +- .../java/foreign/TestUpcallStructScope.java | 18 +- test/jdk/java/foreign/TestVarArgs.java | 17 +- .../foreign/TestVarHandleCombinators.java | 10 +- test/jdk/java/foreign/ThrowingUpcall.java | 95 -- test/jdk/java/foreign/UpcallTestHelper.java | 18 +- .../arraystructs/TestArrayStructs.java | 2 +- .../TestLayoutEquality.java | 18 +- .../TestLinuxAArch64CallArranger.java | 3 +- .../TestMacOsAArch64CallArranger.java | 3 +- .../callarranger/TestRISCV64CallArranger.java | 34 +- .../callarranger/TestSysVCallArranger.java | 65 +- .../TestWindowsAArch64CallArranger.java | 6 +- .../callarranger/TestWindowsCallArranger.java | 6 +- .../platform}/PlatformLayouts.java | 107 +- .../TestCaptureCallState.java | 11 +- .../channels/AbstractChannelsTest.java | 15 +- .../channels/TestAsyncSocketChannels.java | 26 +- .../foreign/channels/TestSocketChannels.java | 23 +- .../foreign/dontrelease/TestDontRelease.java | 8 +- .../openjdk/foreigntest/PanamaMainDirect.java | 6 +- .../openjdk/foreigntest/PanamaMainInvoke.java | 10 +- .../foreigntest/PanamaMainReflection.java | 8 +- .../handle/invoker/MethodHandleInvoker.java | 12 +- .../handle/lookup/MethodHandleLookup.java | 27 +- test/jdk/java/foreign/libAddressDereference.c | 37 + test/jdk/java/foreign/nested/TestNested.java | 2 +- .../java/foreign/normalize/TestNormalize.java | 16 +- .../foreign/stackwalk/TestAsyncStackWalk.java | 6 +- .../stackwalk/TestReentrantUpcalls.java | 4 +- .../java/foreign/stackwalk/TestStackWalk.java | 6 +- .../jdk/java/foreign/trivial/TestTrivial.java | 98 ++ .../foreign/trivial/TestTrivialUpcall.java | 64 ++ .../java/foreign/trivial/libTrivial.c} | 34 +- .../foreign/upcalldeopt/TestUpcallDeopt.java | 6 +- test/jdk/java/foreign/valist/VaListTest.java | 933 --------------- test/jdk/java/foreign/valist/libVaList.c | 188 --- .../AttachCurrentThread/ImplicitAttach.java | 6 +- .../invoke/VarHandles/VarHandleTestExact.java | 10 +- .../channels/FileChannel/LargeMapTest.java | 4 +- .../FileChannel/MapToMemorySegmentTest.java | 13 +- .../java/util/stream/SpliteratorTest.java | 2 +- .../vector/AbstractVectorLoadStoreTest.java | 6 +- .../vector/Byte128VectorLoadStoreTests.java | 18 +- .../vector/Byte256VectorLoadStoreTests.java | 18 +- .../vector/Byte512VectorLoadStoreTests.java | 18 +- .../vector/Byte64VectorLoadStoreTests.java | 18 +- .../vector/ByteMaxVectorLoadStoreTests.java | 18 +- .../vector/Double128VectorLoadStoreTests.java | 18 +- .../vector/Double256VectorLoadStoreTests.java | 18 +- .../vector/Double512VectorLoadStoreTests.java | 18 +- .../vector/Double64VectorLoadStoreTests.java | 18 +- .../vector/DoubleMaxVectorLoadStoreTests.java | 18 +- .../vector/Float128VectorLoadStoreTests.java | 18 +- .../vector/Float256VectorLoadStoreTests.java | 18 +- .../vector/Float512VectorLoadStoreTests.java | 18 +- .../vector/Float64VectorLoadStoreTests.java | 18 +- .../vector/FloatMaxVectorLoadStoreTests.java | 18 +- .../vector/Int128VectorLoadStoreTests.java | 18 +- .../vector/Int256VectorLoadStoreTests.java | 18 +- .../vector/Int512VectorLoadStoreTests.java | 18 +- .../vector/Int64VectorLoadStoreTests.java | 18 +- .../vector/IntMaxVectorLoadStoreTests.java | 18 +- .../vector/Long128VectorLoadStoreTests.java | 18 +- .../vector/Long256VectorLoadStoreTests.java | 18 +- .../vector/Long512VectorLoadStoreTests.java | 18 +- .../vector/Long64VectorLoadStoreTests.java | 18 +- .../vector/LongMaxVectorLoadStoreTests.java | 18 +- .../vector/Short128VectorLoadStoreTests.java | 18 +- .../vector/Short256VectorLoadStoreTests.java | 18 +- .../vector/Short512VectorLoadStoreTests.java | 18 +- .../vector/Short64VectorLoadStoreTests.java | 18 +- .../vector/ShortMaxVectorLoadStoreTests.java | 18 +- .../templates/X-LoadStoreTest.java.template | 18 +- .../lang/foreign/BulkMismatchAcquire.java | 165 --- .../bench/java/lang/foreign/BulkOps.java | 19 +- .../bench/java/lang/foreign/CLayouts.java | 9 +- .../lang/foreign/CallOverheadConstant.java | 10 + .../java/lang/foreign/CallOverheadHelper.java | 47 +- .../lang/foreign/CallOverheadVirtual.java | 10 + .../bench/java/lang/foreign/JavaLayouts.java | 1 - .../bench/java/lang/foreign/LinkUpcall.java | 7 +- .../java/lang/foreign/LoopOverConstant.java | 6 +- .../bench/java/lang/foreign/LoopOverNew.java | 17 +- .../lang/foreign/LoopOverNonConstant.java | 4 +- .../lang/foreign/LoopOverNonConstantFP.java | 6 +- .../lang/foreign/LoopOverNonConstantHeap.java | 5 +- .../foreign/LoopOverNonConstantMapped.java | 4 +- .../foreign/LoopOverNonConstantShared.java | 4 +- .../java/lang/foreign/LoopOverOfAddress.java | 8 +- .../foreign/LoopOverPollutedSegments.java | 10 +- .../java/lang/foreign/LoopOverSlice.java | 4 +- .../lang/foreign/MemorySegmentVsBits.java | 158 +++ .../java/lang/foreign/MemorySessionClose.java | 11 +- .../bench/java/lang/foreign/ParallelSum.java | 10 +- .../java/lang/foreign/PointerInvoke.java | 54 +- .../bench/java/lang/foreign/QSort.java | 13 +- .../bench/java/lang/foreign/StrLenTest.java | 63 +- .../java/lang/foreign/TestLoadBytes.java | 5 +- .../java/lang/foreign/UnrolledAccess.java | 8 +- .../bench/java/lang/foreign/Upcalls.java | 9 +- .../bench/java/lang/foreign/VaList.java | 84 -- .../java/lang/foreign/VarHandleExact.java | 4 +- .../openjdk/bench/java/lang/foreign/libPtr.c | 18 +- .../lang/foreign/pointers/NativeType.java | 6 +- .../lang/foreign/pointers/PointerBench.java | 7 +- .../foreign/points/support/PanamaPoint.java | 11 +- .../vector/MemorySegmentVectorAccess.java | 8 +- .../incubator/vector/TestLoadStoreBytes.java | 11 +- .../incubator/vector/TestLoadStoreShorts.java | 11 +- 267 files changed, 6947 insertions(+), 8029 deletions(-) create mode 100644 make/devkit/createLibffiBundle.sh create mode 100644 src/java.base/share/classes/java/lang/foreign/AddressLayout.java delete mode 100644 src/java.base/share/classes/java/lang/foreign/SegmentScope.java delete mode 100644 src/java.base/share/classes/java/lang/foreign/VaList.java delete mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java delete mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java delete mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64VaList.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIABI.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIStatus.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java delete mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64VaList.java delete mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java delete mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java create mode 100644 src/java.base/share/classes/jdk/internal/vm/ForeignLinkerSupport.java create mode 100644 src/java.base/share/native/libfallbackLinker/fallbackLinker.c create mode 100644 src/java.base/share/native/libjava/ForeignLinkerSupport.c create mode 100644 test/jdk/java/foreign/TestAddressDereference.java create mode 100644 test/jdk/java/foreign/TestDereferencePath.java delete mode 100644 test/jdk/java/foreign/ThrowingUpcall.java rename test/jdk/java/foreign/{ => callarranger}/TestLayoutEquality.java (81%) rename {src/java.base/share/classes/jdk/internal/foreign => test/jdk/java/foreign/callarranger/platform}/PlatformLayouts.java (71%) create mode 100644 test/jdk/java/foreign/libAddressDereference.c create mode 100644 test/jdk/java/foreign/trivial/TestTrivial.java create mode 100644 test/jdk/java/foreign/trivial/TestTrivialUpcall.java rename test/{micro/org/openjdk/bench/java/lang/foreign/libVaList.c => jdk/java/foreign/trivial/libTrivial.c} (69%) delete mode 100644 test/jdk/java/foreign/valist/VaListTest.java delete mode 100644 test/jdk/java/foreign/valist/libVaList.c delete mode 100644 test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java create mode 100644 test/micro/org/openjdk/bench/java/lang/foreign/MemorySegmentVsBits.java delete mode 100644 test/micro/org/openjdk/bench/java/lang/foreign/VaList.java diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 7c5d7b13ada..6afa36ac18d 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -223,6 +223,10 @@ JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER # LeakSanitizer JDKOPT_SETUP_LEAK_SANITIZER +# Fallback linker +# This needs to go before 'LIB_DETERMINE_DEPENDENCIES' +JDKOPT_SETUP_FALLBACK_LINKER + ############################################################################### # # Check dependencies for external and internal libraries. diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index a76fdab5ae5..f08cc6ddd41 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -903,3 +903,22 @@ AC_DEFUN([JDKOPT_SETUP_MACOSX_SIGNING], AC_SUBST(MACOSX_CODESIGN_MODE) fi ]) + +################################################################################ +# +# fallback linker +# +AC_DEFUN_ONCE([JDKOPT_SETUP_FALLBACK_LINKER], +[ + FALLBACK_LINKER_DEFAULT=false + + if HOTSPOT_CHECK_JVM_VARIANT(zero); then + FALLBACK_LINKER_DEFAULT=true + fi + + UTIL_ARG_ENABLE(NAME: fallback-linker, DEFAULT: $FALLBACK_LINKER_DEFAULT, + RESULT: ENABLE_FALLBACK_LINKER, + DESC: [enable libffi-based fallback implementation of java.lang.foreign.Linker], + CHECKING_MSG: [if fallback linker enabled]) + AC_SUBST(ENABLE_FALLBACK_LINKER) +]) diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4 index 9e746b470c9..a1fc81564b1 100644 --- a/make/autoconf/libraries.m4 +++ b/make/autoconf/libraries.m4 @@ -82,7 +82,7 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES], fi # Check if ffi is needed - if HOTSPOT_CHECK_JVM_VARIANT(zero); then + if HOTSPOT_CHECK_JVM_VARIANT(zero) || test "x$ENABLE_FALLBACK_LINKER" = "xtrue"; then NEEDS_LIB_FFI=true else NEEDS_LIB_FFI=false diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 6be074f95e0..0f85917814e 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -409,6 +409,9 @@ TEST_JOBS?=@TEST_JOBS@ DEFAULT_MAKE_TARGET:=@DEFAULT_MAKE_TARGET@ DEFAULT_LOG:=@DEFAULT_LOG@ +# Fallback linker +ENABLE_FALLBACK_LINKER:=@ENABLE_FALLBACK_LINKER@ + FREETYPE_TO_USE:=@FREETYPE_TO_USE@ FREETYPE_LIBS:=@FREETYPE_LIBS@ FREETYPE_CFLAGS:=@FREETYPE_CFLAGS@ diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index d3b5aa2459d..2c6742c94e7 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -587,11 +587,12 @@ var getJibProfilesProfiles = function (input, common, data) { "linux-x64-zero": { target_os: "linux", target_cpu: "x64", - dependencies: ["devkit", "gtest"], + dependencies: ["devkit", "gtest", "libffi"], configure_args: concat(common.configure_args_64bit, [ "--with-zlib=system", "--with-jvm-variants=zero", - "--enable-libffi-bundling" + "--with-libffi=" + input.get("libffi", "home_path"), + "--enable-libffi-bundling", ]) }, @@ -744,6 +745,40 @@ var getJibProfilesProfiles = function (input, common, data) { common.debug_profile_artifacts(artifactData[name])); }); + // Define artifact just for linux-x64-zero, which is the only one we test on + ["linux-x64"].forEach(function (name) { + var o = artifactData[name] + var pf = o.platform + var jdk_subdir = (o.jdk_subdir != null ? o.jdk_subdir : "jdk-" + data.version); + var jdk_suffix = (o.jdk_suffix != null ? o.jdk_suffix : "tar.gz"); + var zeroName = name + "-zero"; + profiles[zeroName].artifacts = { + jdk: { + local: "bundles/\\(jdk.*bin." + jdk_suffix + "\\)", + remote: [ + "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero." + jdk_suffix, + ], + subdir: jdk_subdir, + exploded: "images/jdk", + }, + test: { + local: "bundles/\\(jdk.*bin-tests.tar.gz\\)", + remote: [ + "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero-tests.tar.gz", + ], + exploded: "images/test" + }, + jdk_symbols: { + local: "bundles/\\(jdk.*bin-symbols.tar.gz\\)", + remote: [ + "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero-symbols.tar.gz", + ], + subdir: jdk_subdir, + exploded: "images/jdk" + }, + }; + }); + buildJdkDep = input.build_os + "-" + input.build_cpu + ".jdk"; docsProfiles = { "docs": { @@ -1234,6 +1269,13 @@ var getJibProfilesDependencies = function (input, common) { ext: "tar.gz", revision: "1.13.0+1.0" }, + + libffi: { + organization: common.organization, + module: "libffi-" + input.build_platform, + ext: "tar.gz", + revision: "3.4.2+1.0" + }, }; return dependencies; diff --git a/make/data/hotspot-symbols/symbols-shared b/make/data/hotspot-symbols/symbols-shared index ab6adf06d4d..c5b13ef1ee8 100644 --- a/make/data/hotspot-symbols/symbols-shared +++ b/make/data/hotspot-symbols/symbols-shared @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, 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 @@ -30,5 +30,6 @@ jio_vsnprintf JNI_CreateJavaVM JNI_GetCreatedJavaVMs JNI_GetDefaultJavaVMInitArgs +JVM_IsForeignLinkerSupported JVM_FindClassFromBootLoader JVM_InitAgentProperties diff --git a/make/devkit/createLibffiBundle.sh b/make/devkit/createLibffiBundle.sh new file mode 100644 index 00000000000..62d714a5148 --- /dev/null +++ b/make/devkit/createLibffiBundle.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# +# Copyright (c) 2023, 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. +# + +# This script generates a libffi bundle. On linux by building it from source +# using a devkit, which should match the devkit used to build the JDK. +# +# Set MAKE_ARGS to add parameters to make. Ex: +# +# $ MAKE_ARGS=-j32 bash createLibffiBundle.sh +# +# The script tries to behave well on multiple invocations, only performing steps +# not already done. To redo a step, manually delete the target files from that +# step. +# +# Note that the libtool and texinfo packages are needed to build libffi +# $ sudo apt install libtool texinfo + +LIBFFI_VERSION=3.4.2 + +BUNDLE_NAME=libffi-$LIBFFI_VERSION.tar.gz + +SCRIPT_FILE="$(basename $0)" +SCRIPT_DIR="$(cd "$(dirname $0)" > /dev/null && pwd)" +OUTPUT_DIR="${SCRIPT_DIR}/../../build/libffi" +SRC_DIR="$OUTPUT_DIR/src" +DOWNLOAD_DIR="$OUTPUT_DIR/download" +INSTALL_DIR="$OUTPUT_DIR/install" +IMAGE_DIR="$OUTPUT_DIR/image" + +USAGE="$0 " + +if [ "$1" = "" ]; then + echo $USAGE + exit 1 +fi +DEVKIT_DIR="$1" + +# Download source distros +mkdir -p $DOWNLOAD_DIR +cd $DOWNLOAD_DIR +SOURCE_TAR=v$LIBFFI_VERSION.tar.gz +if [ ! -f $SOURCE_TAR ]; then + wget https://github.com/libffi/libffi/archive/refs/tags/v$LIBFFI_VERSION.tar.gz +fi + +# Unpack src +mkdir -p $SRC_DIR +cd $SRC_DIR +LIBFFI_DIRNAME=libffi-$LIBFFI_VERSION +LIBFFI_DIR=$SRC_DIR/$LIBFFI_DIRNAME +if [ ! -d $LIBFFI_DIRNAME ]; then + echo "Unpacking $SOURCE_TAR" + tar xf $DOWNLOAD_DIR/$SOURCE_TAR +fi + +# Build +cd $LIBFFI_DIR +if [ ! -e $LIBFFI_DIR/configure ]; then + bash ./autogen.sh +fi +bash ./configure --prefix=$INSTALL_DIR CC=$DEVKIT_DIR/bin/gcc CXX=$DEVKIT_DIR/bin/g++ + +# Run with nice to keep system usable during build. +nice make $MAKE_ARGS install + +mkdir -p $IMAGE_DIR +# Extract what we need into an image +if [ ! -e $IMAGE_DIR/lib/libffi.so ]; then + echo "Copying libffi.so* to image" + mkdir -p $IMAGE_DIR/lib + cp -a $INSTALL_DIR/lib64/libffi.so* $IMAGE_DIR/lib/ +fi +if [ ! -e $IMAGE_DIR/include/ ]; then + echo "Copying include to image" + mkdir -p $IMAGE_DIR/include + cp -a $INSTALL_DIR/include/. $IMAGE_DIR/include/ +fi +if [ ! -e $IMAGE_DIR/$SCRIPT_FILE ]; then + echo "Copying this script to image" + cp -a $SCRIPT_DIR/$SCRIPT_FILE $IMAGE_DIR/ +fi + +# Create bundle +if [ ! -e $OUTPUT_DIR/$BUNDLE_NAME ]; then + echo "Creating $OUTPUT_DIR/$BUNDLE_NAME" + cd $IMAGE_DIR + tar zcf $OUTPUT_DIR/$BUNDLE_NAME * +fi diff --git a/make/modules/java.base/Lib.gmk b/make/modules/java.base/Lib.gmk index 93c0a361671..d6ca2932914 100644 --- a/make/modules/java.base/Lib.gmk +++ b/make/modules/java.base/Lib.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, 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 @@ -215,3 +215,18 @@ $(eval $(call SetupJdkLibrary, BUILD_SYSLOOKUPLIB, \ )) TARGETS += $(BUILD_SYSLOOKUPLIB) + +################################################################################ +# Create fallback linker lib + +ifeq ($(ENABLE_FALLBACK_LINKER), true) + $(eval $(call SetupJdkLibrary, BUILD_LIBFALLBACKLINKER, \ + NAME := fallbackLinker, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBFFI_CFLAGS), \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LIBS := $(LIBFFI_LIBS), \ + )) + + TARGETS += $(BUILD_LIBFALLBACKLINKER) +endif diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp index 9c9d78e8d29..458ab541e32 100644 --- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp @@ -47,6 +47,7 @@ class DowncallStubGenerator : public StubCodeGenerator { bool _needs_return_buffer; int _captured_state_mask; + bool _needs_transition; int _frame_complete; int _frame_size_slots; @@ -60,7 +61,8 @@ public: const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) + int captured_state_mask, + bool needs_transition) : StubCodeGenerator(buffer, PrintMethodHandleStubs), _signature(signature), _num_args(num_args), @@ -70,6 +72,7 @@ public: _output_registers(output_registers), _needs_return_buffer(needs_return_buffer), _captured_state_mask(captured_state_mask), + _needs_transition(needs_transition), _frame_complete(0), _frame_size_slots(0), _oop_maps(NULL) { @@ -100,13 +103,15 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { int code_size = native_invoker_code_base_size + (num_args * native_invoker_size_per_arg); int locs_size = 1; // must be non-zero CodeBuffer code("nep_invoker_blob", code_size, locs_size); DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi, input_registers, output_registers, - needs_return_buffer, captured_state_mask); + needs_return_buffer, captured_state_mask, + needs_transition); g.generate(); code.log_section_sizes("nep_invoker_blob"); @@ -163,7 +168,7 @@ void DowncallStubGenerator::generate() { assert(_abi._shadow_space_bytes == 0, "not expecting shadow space on AArch64"); allocated_frame_size += arg_shuffle.out_arg_bytes(); - bool should_save_return_value = !_needs_return_buffer; + bool should_save_return_value = !_needs_return_buffer && _needs_transition; RegSpiller out_reg_spiller(_output_registers); int spill_offset = -1; @@ -191,7 +196,7 @@ void DowncallStubGenerator::generate() { _frame_size_slots = align_up(framesize + (allocated_frame_size >> LogBytesPerInt), 4); assert(is_even(_frame_size_slots/2), "sp not 16-byte aligned"); - _oop_maps = new OopMapSet(); + _oop_maps = _needs_transition ? new OopMapSet() : nullptr; address start = __ pc(); __ enter(); @@ -201,15 +206,17 @@ void DowncallStubGenerator::generate() { _frame_complete = __ pc() - start; - address the_pc = __ pc(); - __ set_last_Java_frame(sp, rfp, the_pc, tmp1); - OopMap* map = new OopMap(_frame_size_slots, 0); - _oop_maps->add_gc_map(the_pc - start, map); + if (_needs_transition) { + address the_pc = __ pc(); + __ set_last_Java_frame(sp, rfp, the_pc, tmp1); + OopMap* map = new OopMap(_frame_size_slots, 0); + _oop_maps->add_gc_map(the_pc - start, map); - // State transition - __ mov(tmp1, _thread_in_native); - __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); - __ stlrw(tmp1, tmp2); + // State transition + __ mov(tmp1, _thread_in_native); + __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); + __ stlrw(tmp1, tmp2); + } __ block_comment("{ argument shuffle"); arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs); @@ -257,86 +264,89 @@ void DowncallStubGenerator::generate() { ////////////////////////////////////////////////////////////////////////////// - __ mov(tmp1, _thread_in_native_trans); - __ strw(tmp1, Address(rthread, JavaThread::thread_state_offset())); - - // Force this write out before the read below - if (!UseSystemMemoryBarrier) { - __ membar(Assembler::LoadLoad | Assembler::LoadStore | - Assembler::StoreLoad | Assembler::StoreStore); - } - - __ verify_sve_vector_length(tmp1); - Label L_after_safepoint_poll; Label L_safepoint_poll_slow_path; - - __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1); - - __ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset())); - __ cbnzw(tmp1, L_safepoint_poll_slow_path); - - __ bind(L_after_safepoint_poll); - - // change thread state - __ mov(tmp1, _thread_in_Java); - __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); - __ stlrw(tmp1, tmp2); - - __ block_comment("reguard stack check"); Label L_reguard; Label L_after_reguard; - __ ldrb(tmp1, Address(rthread, JavaThread::stack_guard_state_offset())); - __ cmpw(tmp1, StackOverflow::stack_guard_yellow_reserved_disabled); - __ br(Assembler::EQ, L_reguard); - __ bind(L_after_reguard); + if (_needs_transition) { + __ mov(tmp1, _thread_in_native_trans); + __ strw(tmp1, Address(rthread, JavaThread::thread_state_offset())); - __ reset_last_Java_frame(true); + // Force this write out before the read below + if (!UseSystemMemoryBarrier) { + __ membar(Assembler::LoadLoad | Assembler::LoadStore | + Assembler::StoreLoad | Assembler::StoreStore); + } + + __ verify_sve_vector_length(tmp1); + + __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1); + + __ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset())); + __ cbnzw(tmp1, L_safepoint_poll_slow_path); + + __ bind(L_after_safepoint_poll); + + // change thread state + __ mov(tmp1, _thread_in_Java); + __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); + __ stlrw(tmp1, tmp2); + + __ block_comment("reguard stack check"); + __ ldrb(tmp1, Address(rthread, JavaThread::stack_guard_state_offset())); + __ cmpw(tmp1, StackOverflow::stack_guard_yellow_reserved_disabled); + __ br(Assembler::EQ, L_reguard); + __ bind(L_after_reguard); + + __ reset_last_Java_frame(true); + } __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_safepoint_poll_slow_path"); - __ bind(L_safepoint_poll_slow_path); + if (_needs_transition) { + __ block_comment("{ L_safepoint_poll_slow_path"); + __ bind(L_safepoint_poll_slow_path); - if (should_save_return_value) { - // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); - } + if (should_save_return_value) { + // Need to save the native result registers around any runtime calls. + out_reg_spiller.generate_spill(_masm, spill_offset); + } - __ mov(c_rarg0, rthread); - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); - __ lea(tmp1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); - __ blr(tmp1); + __ mov(c_rarg0, rthread); + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); + __ lea(tmp1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); + __ blr(tmp1); - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } - __ b(L_after_safepoint_poll); - __ block_comment("} L_safepoint_poll_slow_path"); + __ b(L_after_safepoint_poll); + __ block_comment("} L_safepoint_poll_slow_path"); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_reguard"); - __ bind(L_reguard); + __ block_comment("{ L_reguard"); + __ bind(L_reguard); - if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_offset); + } + + __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1); + + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } + + __ b(L_after_reguard); + + __ block_comment("} L_reguard"); } - __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1); - - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); - } - - __ b(L_after_reguard); - - __ block_comment("} L_reguard"); - ////////////////////////////////////////////////////////////////////////////// __ flush(); diff --git a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp index 90c1942c32d..2692054bcdc 100644 --- a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2022, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -33,6 +33,10 @@ #include "prims/vmstorage.hpp" #include "utilities/formatBuffer.hpp" +bool ForeignGlobals::is_foreign_linker_supported() { + return true; +} + bool ABIDescriptor::is_volatile_reg(Register reg) const { return _integer_argument_registers.contains(reg) || _integer_additional_volatile_registers.contains(reg); diff --git a/src/hotspot/cpu/arm/downcallLinker_arm.cpp b/src/hotspot/cpu/arm/downcallLinker_arm.cpp index 37b6f43ac14..baee7d7a043 100644 --- a/src/hotspot/cpu/arm/downcallLinker_arm.cpp +++ b/src/hotspot/cpu/arm/downcallLinker_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/arm/foreignGlobals_arm.cpp b/src/hotspot/cpu/arm/foreignGlobals_arm.cpp index 5438cbe5cd6..d3a318536bd 100644 --- a/src/hotspot/cpu/arm/foreignGlobals_arm.cpp +++ b/src/hotspot/cpu/arm/foreignGlobals_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,6 +29,10 @@ class MacroAssembler; +bool ForeignGlobals::is_foreign_linker_supported() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); return {}; diff --git a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp index 13679cf6669..0a4c2c31086 100644 --- a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020 SAP SE. All rights reserved. - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp b/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp index b685023656d..cf9904cfae5 100644 --- a/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp +++ b/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 SAP SE. All rights reserved. + * Copyright (c) 2020, 2023, SAP SE. All rights reserved. * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,6 +29,10 @@ class MacroAssembler; +bool ForeignGlobals::is_foreign_linker_supported() { + return false; +} + // Stubbed out, implement later const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); diff --git a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp index 994402bc2b4..6f0e4e97246 100644 --- a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp @@ -48,6 +48,7 @@ class DowncallStubGenerator : public StubCodeGenerator { bool _needs_return_buffer; int _captured_state_mask; + bool _needs_transition; int _frame_complete; int _frame_size_slots; @@ -61,7 +62,8 @@ public: const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) + int captured_state_mask, + bool needs_transition) : StubCodeGenerator(buffer, PrintMethodHandleStubs), _signature(signature), _num_args(num_args), @@ -71,6 +73,7 @@ public: _output_registers(output_registers), _needs_return_buffer(needs_return_buffer), _captured_state_mask(captured_state_mask), + _needs_transition(needs_transition), _frame_complete(0), _frame_size_slots(0), _oop_maps(nullptr) { @@ -101,13 +104,15 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { int code_size = native_invoker_code_base_size + (num_args * native_invoker_size_per_arg); int locs_size = 1; // must be non-zero CodeBuffer code("nep_invoker_blob", code_size, locs_size); DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi, input_registers, output_registers, - needs_return_buffer, captured_state_mask); + needs_return_buffer, captured_state_mask, + needs_transition); g.generate(); code.log_section_sizes("nep_invoker_blob"); @@ -160,7 +165,7 @@ void DowncallStubGenerator::generate() { assert(_abi._shadow_space_bytes == 0, "not expecting shadow space on RISCV64"); allocated_frame_size += arg_shuffle.out_arg_bytes(); - bool should_save_return_value = !_needs_return_buffer; + bool should_save_return_value = !_needs_return_buffer && _needs_transition; RegSpiller out_reg_spiller(_output_registers); int spill_offset = -1; @@ -190,7 +195,7 @@ void DowncallStubGenerator::generate() { _frame_size_slots += framesize + (allocated_frame_size >> LogBytesPerInt); assert(is_even(_frame_size_slots / 2), "sp not 16-byte aligned"); - _oop_maps = new OopMapSet(); + _oop_maps = _needs_transition ? new OopMapSet() : nullptr; address start = __ pc(); __ enter(); @@ -200,17 +205,19 @@ void DowncallStubGenerator::generate() { _frame_complete = __ pc() - start; // frame build complete. - __ block_comment("{ thread java2native"); - address the_pc = __ pc(); - __ set_last_Java_frame(sp, fp, the_pc, t0); - OopMap* map = new OopMap(_frame_size_slots, 0); - _oop_maps->add_gc_map(the_pc - start, map); + if (_needs_transition) { + __ block_comment("{ thread java2native"); + address the_pc = __ pc(); + __ set_last_Java_frame(sp, fp, the_pc, t0); + OopMap* map = new OopMap(_frame_size_slots, 0); + _oop_maps->add_gc_map(the_pc - start, map); - // State transition - __ mv(t0, _thread_in_native); - __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); - __ block_comment("} thread java2native"); + // State transition + __ mv(t0, _thread_in_native); + __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); + __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); + __ block_comment("} thread java2native"); + } __ block_comment("{ argument shuffle"); arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs); @@ -260,80 +267,85 @@ void DowncallStubGenerator::generate() { ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ thread native2java"); - __ mv(t0, _thread_in_native_trans); - __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); - - // Force this write out before the read below - if (!UseSystemMemoryBarrier) { - __ membar(MacroAssembler::AnyAny); - } - Label L_after_safepoint_poll; Label L_safepoint_poll_slow_path; - __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */); - __ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset())); - __ bnez(t0, L_safepoint_poll_slow_path); - - __ bind(L_after_safepoint_poll); - - __ mv(t0, _thread_in_Java); - __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); - - __ block_comment("reguard stack check"); Label L_reguard; Label L_after_reguard; - __ lbu(t0, Address(xthread, JavaThread::stack_guard_state_offset())); - __ mv(t1, StackOverflow::stack_guard_yellow_reserved_disabled); - __ beq(t0, t1, L_reguard); - __ bind(L_after_reguard); + if (_needs_transition) { + __ block_comment("{ thread native2java"); + __ mv(t0, _thread_in_native_trans); + __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); - __ reset_last_Java_frame(true); - __ block_comment("} thread native2java"); + // Force this write out before the read below + if (!UseSystemMemoryBarrier) { + __ membar(MacroAssembler::AnyAny); + } + + __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */); + __ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset())); + __ bnez(t0, L_safepoint_poll_slow_path); + + __ bind(L_after_safepoint_poll); + + // change thread state + __ mv(t0, _thread_in_Java); + __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); + __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); + + __ block_comment("reguard stack check"); + __ lbu(t0, Address(xthread, JavaThread::stack_guard_state_offset())); + __ mv(t1, StackOverflow::stack_guard_yellow_reserved_disabled); + __ beq(t0, t1, L_reguard); + __ bind(L_after_reguard); + + __ reset_last_Java_frame(true); + __ block_comment("} thread native2java"); + } __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_safepoint_poll_slow_path"); - __ bind(L_safepoint_poll_slow_path); + if (_needs_transition) { + __ block_comment("{ L_safepoint_poll_slow_path"); + __ bind(L_safepoint_poll_slow_path); - if (should_save_return_value) { - // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); - } + if (should_save_return_value) { + // Need to save the native result registers around any runtime calls. + out_reg_spiller.generate_spill(_masm, spill_offset); + } - __ mv(c_rarg0, xthread); - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); - __ rt_call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)); + __ mv(c_rarg0, xthread); + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); + __ rt_call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)); - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); - } - __ j(L_after_safepoint_poll); - __ block_comment("} L_safepoint_poll_slow_path"); + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } + __ j(L_after_safepoint_poll); + __ block_comment("} L_safepoint_poll_slow_path"); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_reguard"); - __ bind(L_reguard); + __ block_comment("{ L_reguard"); + __ bind(L_reguard); - if (should_save_return_value) { - // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + if (should_save_return_value) { + // Need to save the native result registers around any runtime calls. + out_reg_spiller.generate_spill(_masm, spill_offset); + } + + __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); + + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } + + __ j(L_after_reguard); + __ block_comment("} L_reguard"); } - __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); - - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); - } - - __ j(L_after_reguard); - __ block_comment("} L_reguard"); - ////////////////////////////////////////////////////////////////////////////// __ flush(); diff --git a/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp b/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp index 44cff28b119..7ecfe0c38d3 100644 --- a/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp +++ b/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -44,6 +44,10 @@ bool ABIDescriptor::is_volatile_reg(FloatRegister reg) const { || _float_additional_volatile_registers.contains(reg); } +bool ForeignGlobals::is_foreign_linker_supported() { + return true; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { oop abi_oop = JNIHandles::resolve_non_null(jabi); ABIDescriptor abi; diff --git a/src/hotspot/cpu/s390/downcallLinker_s390.cpp b/src/hotspot/cpu/s390/downcallLinker_s390.cpp index 37b6f43ac14..baee7d7a043 100644 --- a/src/hotspot/cpu/s390/downcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/downcallLinker_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/s390/foreignGlobals_s390.cpp b/src/hotspot/cpu/s390/foreignGlobals_s390.cpp index 5438cbe5cd6..d3a318536bd 100644 --- a/src/hotspot/cpu/s390/foreignGlobals_s390.cpp +++ b/src/hotspot/cpu/s390/foreignGlobals_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,6 +29,10 @@ class MacroAssembler; +bool ForeignGlobals::is_foreign_linker_supported() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); return {}; diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp index 3f1241970f2..aabe49a3002 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -31,7 +31,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp index 787a66eb9e5..6c6b44a158b 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp @@ -46,6 +46,7 @@ class DowncallStubGenerator : public StubCodeGenerator { bool _needs_return_buffer; int _captured_state_mask; + bool _needs_transition; int _frame_complete; int _frame_size_slots; @@ -59,7 +60,8 @@ public: const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) + int captured_state_mask, + bool needs_transition) : StubCodeGenerator(buffer, PrintMethodHandleStubs), _signature(signature), _num_args(num_args), @@ -69,6 +71,7 @@ public: _output_registers(output_registers), _needs_return_buffer(needs_return_buffer), _captured_state_mask(captured_state_mask), + _needs_transition(needs_transition), _frame_complete(0), _frame_size_slots(0), _oop_maps(nullptr) { @@ -99,13 +102,15 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { int code_size = native_invoker_code_base_size + (num_args * native_invoker_size_per_arg); int locs_size = 1; // can not be zero CodeBuffer code("nep_invoker_blob", code_size, locs_size); DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi, input_registers, output_registers, - needs_return_buffer, captured_state_mask); + needs_return_buffer, captured_state_mask, + needs_transition); g.generate(); code.log_section_sizes("nep_invoker_blob"); @@ -161,7 +166,7 @@ void DowncallStubGenerator::generate() { allocated_frame_size += arg_shuffle.out_arg_bytes(); // when we don't use a return buffer we need to spill the return value around our slow path calls - bool should_save_return_value = !_needs_return_buffer; + bool should_save_return_value = !_needs_return_buffer && _needs_transition; RegSpiller out_reg_spiller(_output_registers); int spill_rsp_offset = -1; @@ -190,7 +195,7 @@ void DowncallStubGenerator::generate() { _frame_size_slots += framesize_base + (allocated_frame_size >> LogBytesPerInt); assert(is_even(_frame_size_slots/2), "sp not 16-byte aligned"); - _oop_maps = new OopMapSet(); + _oop_maps = _needs_transition ? new OopMapSet() : nullptr; address start = __ pc(); __ enter(); @@ -200,16 +205,17 @@ void DowncallStubGenerator::generate() { _frame_complete = __ pc() - start; - address the_pc = __ pc(); + if (_needs_transition) { + __ block_comment("{ thread java2native"); + address the_pc = __ pc(); + __ set_last_Java_frame(rsp, rbp, (address)the_pc, rscratch1); + OopMap* map = new OopMap(_frame_size_slots, 0); + _oop_maps->add_gc_map(the_pc - start, map); - __ block_comment("{ thread java2native"); - __ set_last_Java_frame(rsp, rbp, (address)the_pc, rscratch1); - OopMap* map = new OopMap(_frame_size_slots, 0); - _oop_maps->add_gc_map(the_pc - start, map); - - // State transition - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native); - __ block_comment("} thread java2native"); + // State transition + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native); + __ block_comment("} thread java2native"); + } __ block_comment("{ argument shuffle"); arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs); @@ -263,93 +269,95 @@ void DowncallStubGenerator::generate() { ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ thread native2java"); - __ restore_cpu_control_state_after_jni(rscratch1); - - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans); - - // Force this write out before the read below - if (!UseSystemMemoryBarrier) { - __ membar(Assembler::Membar_mask_bits( - Assembler::LoadLoad | Assembler::LoadStore | - Assembler::StoreLoad | Assembler::StoreStore)); - } - Label L_after_safepoint_poll; Label L_safepoint_poll_slow_path; - - __ safepoint_poll(L_safepoint_poll_slow_path, r15_thread, true /* at_return */, false /* in_nmethod */); - __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); - __ jcc(Assembler::notEqual, L_safepoint_poll_slow_path); - - __ bind(L_after_safepoint_poll); - - // change thread state - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); - - __ block_comment("reguard stack check"); Label L_reguard; Label L_after_reguard; - __ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), StackOverflow::stack_guard_yellow_reserved_disabled); - __ jcc(Assembler::equal, L_reguard); - __ bind(L_after_reguard); + if (_needs_transition) { + __ block_comment("{ thread native2java"); + __ restore_cpu_control_state_after_jni(rscratch1); - __ reset_last_Java_frame(r15_thread, true); - __ block_comment("} thread native2java"); + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans); + + // Force this write out before the read below + if (!UseSystemMemoryBarrier) { + __ membar(Assembler::Membar_mask_bits( + Assembler::LoadLoad | Assembler::LoadStore | + Assembler::StoreLoad | Assembler::StoreStore)); + } + + __ safepoint_poll(L_safepoint_poll_slow_path, r15_thread, true /* at_return */, false /* in_nmethod */); + __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::notEqual, L_safepoint_poll_slow_path); + + __ bind(L_after_safepoint_poll); + + // change thread state + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); + + __ block_comment("reguard stack check"); + __ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), StackOverflow::stack_guard_yellow_reserved_disabled); + __ jcc(Assembler::equal, L_reguard); + __ bind(L_after_reguard); + + __ reset_last_Java_frame(r15_thread, true); + __ block_comment("} thread native2java"); + } __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_safepoint_poll_slow_path"); - __ bind(L_safepoint_poll_slow_path); - __ vzeroupper(); + if (_needs_transition) { + __ block_comment("{ L_safepoint_poll_slow_path"); + __ bind(L_safepoint_poll_slow_path); + __ vzeroupper(); - if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + } - __ mov(c_rarg0, r15_thread); - __ mov(r12, rsp); // remember sp - __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows - __ andptr(rsp, -16); // align stack as required by ABI - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); - __ mov(rsp, r12); // restore sp - __ reinit_heapbase(); + __ mov(c_rarg0, r15_thread); + __ mov(r12, rsp); // remember sp + __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows + __ andptr(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); + __ mov(rsp, r12); // restore sp + __ reinit_heapbase(); - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + } - __ jmp(L_after_safepoint_poll); - __ block_comment("} L_safepoint_poll_slow_path"); + __ jmp(L_after_safepoint_poll); + __ block_comment("} L_safepoint_poll_slow_path"); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_reguard"); - __ bind(L_reguard); - __ vzeroupper(); + __ block_comment("{ L_reguard"); + __ bind(L_reguard); + __ vzeroupper(); - if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + } + + __ mov(r12, rsp); // remember sp + __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows + __ andptr(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); + __ mov(rsp, r12); // restore sp + __ reinit_heapbase(); + + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + } + + __ jmp(L_after_reguard); + + __ block_comment("} L_reguard"); } - - __ mov(r12, rsp); // remember sp - __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows - __ andptr(rsp, -16); // align stack as required by ABI - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); - __ mov(rsp, r12); // restore sp - __ reinit_heapbase(); - - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); - } - - __ jmp(L_after_reguard); - - __ block_comment("} L_reguard"); - ////////////////////////////////////////////////////////////////////////////// __ flush(); diff --git a/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp b/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp index 8a31955f4d1..3752bf577d5 100644 --- a/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp +++ b/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -28,6 +28,10 @@ class MacroAssembler; +bool ForeignGlobals::is_foreign_linker_supported() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); return {}; diff --git a/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp b/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp index 74afbe4fd61..8710d4f79f9 100644 --- a/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp +++ b/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -30,6 +30,10 @@ #include "runtime/sharedRuntime.hpp" #include "utilities/formatBuffer.hpp" +bool ForeignGlobals::is_foreign_linker_supported() { + return true; +} + bool ABIDescriptor::is_volatile_reg(Register reg) const { return _integer_argument_registers.contains(reg) || _integer_additional_volatile_registers.contains(reg); diff --git a/src/hotspot/cpu/zero/downcallLinker_zero.cpp b/src/hotspot/cpu/zero/downcallLinker_zero.cpp index 3f1241970f2..aabe49a3002 100644 --- a/src/hotspot/cpu/zero/downcallLinker_zero.cpp +++ b/src/hotspot/cpu/zero/downcallLinker_zero.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -31,7 +31,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/zero/foreignGlobals_zero.cpp b/src/hotspot/cpu/zero/foreignGlobals_zero.cpp index 7c35da7e3e0..2cd83af6b6e 100644 --- a/src/hotspot/cpu/zero/foreignGlobals_zero.cpp +++ b/src/hotspot/cpu/zero/foreignGlobals_zero.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -28,6 +28,10 @@ class MacroAssembler; +bool ForeignGlobals::is_foreign_linker_supported() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { ShouldNotCallThis(); return {}; diff --git a/src/hotspot/cpu/zero/globalDefinitions_zero.hpp b/src/hotspot/cpu/zero/globalDefinitions_zero.hpp index 9db2060b8dd..271d95ee72c 100644 --- a/src/hotspot/cpu/zero/globalDefinitions_zero.hpp +++ b/src/hotspot/cpu/zero/globalDefinitions_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright 2009, 2021, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -32,7 +32,7 @@ #define SUPPORT_MONITOR_COUNT -#ifndef FFI_GO_CLOSURES +#ifdef __APPLE__ #define FFI_GO_CLOSURES 0 #endif diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 55e640cd8b0..d18c8d1f6de 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -174,6 +174,9 @@ JVM_IsPreviewEnabled(void); JNIEXPORT jboolean JNICALL JVM_IsContinuationsSupported(void); +JNIEXPORT jboolean JNICALL +JVM_IsForeignLinkerSupported(void); + JNIEXPORT void JNICALL JVM_InitializeFromArchive(JNIEnv* env, jclass cls); diff --git a/src/hotspot/share/prims/downcallLinker.cpp b/src/hotspot/share/prims/downcallLinker.cpp index ec20fd17d80..b2d5ae2e551 100644 --- a/src/hotspot/share/prims/downcallLinker.cpp +++ b/src/hotspot/share/prims/downcallLinker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -41,12 +41,12 @@ void DowncallLinker::capture_state(int32_t* value_ptr, int captured_state_mask) #ifdef _WIN64 if (captured_state_mask & GET_LAST_ERROR) { *value_ptr = GetLastError(); - value_ptr++; } + value_ptr++; if (captured_state_mask & WSA_GET_LAST_ERROR) { *value_ptr = WSAGetLastError(); - value_ptr++; } + value_ptr++; #endif if (captured_state_mask & ERRNO) { *value_ptr = errno; diff --git a/src/hotspot/share/prims/downcallLinker.hpp b/src/hotspot/share/prims/downcallLinker.hpp index 86849c05158..6840a3c7f69 100644 --- a/src/hotspot/share/prims/downcallLinker.hpp +++ b/src/hotspot/share/prims/downcallLinker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -37,7 +37,8 @@ public: const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask); + int captured_state_mask, + bool needs_transition); static void capture_state(int32_t* value_ptr, int captured_state_mask); }; diff --git a/src/hotspot/share/prims/foreignGlobals.hpp b/src/hotspot/share/prims/foreignGlobals.hpp index d0160f23226..009fd974d0b 100644 --- a/src/hotspot/share/prims/foreignGlobals.hpp +++ b/src/hotspot/share/prims/foreignGlobals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -76,6 +76,8 @@ private: static void parse_register_array(objArrayOop jarray, StorageType type_index, GrowableArray& array, T (*converter)(int)); public: + static bool is_foreign_linker_supported(); + static const ABIDescriptor parse_abi_descriptor(jobject jabi); static const CallRegs parse_call_regs(jobject jconv); static VMStorage parse_vmstorage(oop storage); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 221c5b95d70..2a3420a8fbb 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -63,6 +63,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" +#include "prims/foreignGlobals.hpp" #include "prims/jvm_misc.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.inline.hpp" @@ -3462,6 +3463,10 @@ JVM_LEAF(jboolean, JVM_IsContinuationsSupported(void)) return VMContinuations ? JNI_TRUE : JNI_FALSE; JVM_END +JVM_LEAF(jboolean, JVM_IsForeignLinkerSupported(void)) + return ForeignGlobals::is_foreign_linker_supported() ? JNI_TRUE : JNI_FALSE; +JVM_END + // String support /////////////////////////////////////////////////////////////////////////// JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) diff --git a/src/hotspot/share/prims/nativeEntryPoint.cpp b/src/hotspot/share/prims/nativeEntryPoint.cpp index 1a7ba6fe67b..ef962f8ee41 100644 --- a/src/hotspot/share/prims/nativeEntryPoint.cpp +++ b/src/hotspot/share/prims/nativeEntryPoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -37,7 +37,8 @@ JNI_ENTRY(jlong, NEP_makeDowncallStub(JNIEnv* env, jclass _unused, jobject method_type, jobject jabi, jobjectArray arg_moves, jobjectArray ret_moves, - jboolean needs_return_buffer, jint captured_state_mask)) + jboolean needs_return_buffer, jint captured_state_mask, + jboolean needs_transition)) ResourceMark rm; const ABIDescriptor abi = ForeignGlobals::parse_abi_descriptor(jabi); @@ -77,7 +78,8 @@ JNI_ENTRY(jlong, NEP_makeDowncallStub(JNIEnv* env, jclass _unused, jobject metho return (jlong) DowncallLinker::make_downcall_stub(basic_type, pslots, ret_bt, abi, input_regs, output_regs, - needs_return_buffer, captured_state_mask)->code_begin(); + needs_return_buffer, captured_state_mask, + needs_transition)->code_begin(); JNI_END JNI_ENTRY(jboolean, NEP_freeDowncallStub(JNIEnv* env, jclass _unused, jlong invoker)) @@ -97,7 +99,7 @@ JNI_END #define VM_STORAGE_ARR "[Ljdk/internal/foreign/abi/VMStorage;" static JNINativeMethod NEP_methods[] = { - {CC "makeDowncallStub", CC "(" METHOD_TYPE ABI_DESC VM_STORAGE_ARR VM_STORAGE_ARR "ZI)J", FN_PTR(NEP_makeDowncallStub)}, + {CC "makeDowncallStub", CC "(" METHOD_TYPE ABI_DESC VM_STORAGE_ARR VM_STORAGE_ARR "ZIZ)J", FN_PTR(NEP_makeDowncallStub)}, {CC "freeDowncallStub0", CC "(J)Z", FN_PTR(NEP_freeDowncallStub)}, }; diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index 13ae00fa027..7be41f7447f 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -75,6 +75,7 @@ JavaThread* UpcallLinker::maybe_attach_and_get_thread() { // modelled after JavaCallWrapper::JavaCallWrapper JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { JavaThread* thread = maybe_attach_and_get_thread(); + guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall"); context->thread = thread; assert(thread->can_call_java(), "must be able to call Java"); diff --git a/src/java.base/share/classes/java/lang/foreign/AddressLayout.java b/src/java.base/share/classes/java/lang/foreign/AddressLayout.java new file mode 100644 index 00000000000..fc98f558133 --- /dev/null +++ b/src/java.base/share/classes/java/lang/foreign/AddressLayout.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2023, 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 java.lang.foreign; + +import jdk.internal.foreign.layout.ValueLayouts; +import jdk.internal.javac.PreviewFeature; +import jdk.internal.reflect.CallerSensitive; + +import java.lang.foreign.Linker.Option; +import java.lang.invoke.MethodHandle; +import java.nio.ByteOrder; +import java.util.Optional; + +/** + * A value layout used to model the address of some region of memory. The carrier associated with an address layout is + * {@code MemorySegment.class}. The size and alignment of an address layout are platform dependent + * (e.g. on a 64-bit platform, the size and alignment of an address layout are set to 64 bits). + *

+ * An address layout may optionally feature a {@linkplain #targetLayout() target layout}. An address layout with + * target layout {@code T} can be used to model the address of a region of memory whose layout is {@code T}. + * For instance, an address layout with target layout {@link ValueLayout#JAVA_INT} can be used to model the address + * of a region of memory that is 4 bytes long. Specifying a target layout can be useful in the following situations: + *

    + *
  • When accessing a memory segment that has been obtained by reading an address from another + * memory segment, e.g. using {@link MemorySegment#getAtIndex(AddressLayout, long)};
  • + *
  • When creating a downcall method handle, using {@link Linker#downcallHandle(FunctionDescriptor, Option...)}; + *
  • When creating an upcall stub, using {@link Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...)}. + *
+ * + * @see #ADDRESS + * @see #ADDRESS_UNALIGNED + * @since 19 + */ +@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) +public sealed interface AddressLayout extends ValueLayout permits ValueLayouts.OfAddressImpl { + + /** + * {@inheritDoc} + */ + @Override + AddressLayout withName(String name); + + /** + * {@inheritDoc} + */ + @Override + AddressLayout withoutName(); + + /** + * {@inheritDoc} + */ + @Override + AddressLayout withBitAlignment(long bitAlignment); + + /** + * {@inheritDoc} + */ + @Override + AddressLayout withOrder(ByteOrder order); + + /** + * Returns an address layout with the same carrier, alignment constraint, name and order as this address layout, + * but associated with the specified target layout. The returned address layout allows raw addresses to be accessed + * as {@linkplain MemorySegment memory segments} whose size is set to the size of the specified layout. Moreover, + * if the accessed raw address is not compatible with the alignment constraint in the provided layout, + * {@linkplain IllegalArgumentException} will be thrown. + * @apiNote + * This method can also be used to create an address layout which, when used, creates native memory + * segments with maximal size (e.g. {@linkplain Long#MAX_VALUE}). This can be done by using a target sequence + * layout with unspecified size, as follows: + * {@snippet lang = java: + * AddressLayout addressLayout = ... + * AddressLayout unboundedLayout = addressLayout.withTargetLayout( + * MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE)); + *} + *

+ * This method is restricted. + * Restricted methods are unsafe, and, if used incorrectly, their use might crash + * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on + * restricted methods, and use safe and supported functionalities, where possible. + * + * @param layout the target layout. + * @return an address layout with same characteristics as this layout, but with the provided target layout. + * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. + * @see #targetLayout() + */ + @CallerSensitive + AddressLayout withTargetLayout(MemoryLayout layout); + + /** + * Returns an address layout with the same carrier, alignment constraint, name and order as this address layout, + * but without any specified target layout. + *

+ * This can be useful to compare two address layouts that have different target layouts, but are otherwise equal. + * + * @return an address layout with same characteristics as this layout, but with no target layout. + * @see #targetLayout() + */ + AddressLayout withoutTargetLayout(); + + /** + * {@return the target layout associated with this address layout (if any)}. + */ + Optional targetLayout(); + +} diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java index 2fff341e658..287f466b7f7 100644 --- a/src/java.base/share/classes/java/lang/foreign/Arena.java +++ b/src/java.base/share/classes/java/lang/foreign/Arena.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -27,40 +27,105 @@ package java.lang.foreign; import jdk.internal.foreign.MemorySessionImpl; import jdk.internal.javac.PreviewFeature; +import jdk.internal.ref.CleanerFactory; + +import java.lang.foreign.MemorySegment.Scope; /** - * An arena controls the lifecycle of memory segments, providing both flexible allocation and timely deallocation. + * An arena controls the lifecycle of native memory segments, providing both flexible allocation and timely deallocation. *

- * An arena has a {@linkplain #scope() scope}, called the arena scope. When the arena is {@linkplain #close() closed}, - * the arena scope is no longer {@linkplain SegmentScope#isAlive() alive}. As a result, all the - * segments associated with the arena scope are invalidated, safely and atomically, their backing memory regions are - * deallocated (where applicable) and can no longer be accessed after the arena is closed: + * An arena has a {@linkplain MemorySegment.Scope scope} - the arena scope. All the segments allocated + * by the arena are associated with the arena scope. As such, the arena determines the temporal bounds + * of all the memory segments allocated by it. + *

+ * Moreover, an arena also determines whether access to memory segments allocated by it should be + * {@linkplain MemorySegment#isAccessibleBy(Thread) restricted} to specific threads. + * An arena is a {@link SegmentAllocator} and features several allocation methods that can be used by clients + * to obtain native segments. + *

+ * The simplest arena is the {@linkplain Arena#global() global arena}. The global arena + * features an unbounded lifetime. As such, native segments allocated with the global arena are always + * accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the + * global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread. + * {@snippet lang = java: + * MemorySegment segment = Arena.global().allocate(100, 1); + * ... + * // segment is never deallocated! + *} + *

+ * Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena + * which features a bounded lifetime that is managed, automatically, by the garbage collector. As such, the regions + * of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time + * after the automatic arena (and all the segments allocated by it) become + * unreachable, as shown below: * * {@snippet lang = java: - * try (Arena arena = Arena.openConfined()) { - * MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); - * ... - * } // memory released here + * MemorySegment segment = Arena.ofAuto().allocate(100, 1); + * ... + * segment = null; // the segment region becomes available for deallocation after this point *} - * - * Furthermore, an arena is a {@link SegmentAllocator}. All the segments {@linkplain #allocate(long, long) allocated} by the - * arena are associated with the arena scope. This makes arenas extremely useful when interacting with foreign code, as shown below: + * Memory segments allocated with an automatic arena can also be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread. + *

+ * Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over + * the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this, + * namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature + * bounded lifetimes that are managed manually. For instance, the lifetime of a confined arena starts when the confined + * arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory + * backing memory segments allocated with a confined arena are deallocated when the confined arena is closed. + * When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access + * operations on these segments will fail {@link IllegalStateException}: * * {@snippet lang = java: - * try (Arena arena = Arena.openConfined()) { - * MemorySegment nativeArray = arena.allocateArray(ValueLayout.JAVA_INT, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - * MemorySegment nativeString = arena.allocateUtf8String("Hello!"); - * MemorySegment upcallStub = linker.upcallStub(handle, desc, arena.scope()); + * MemorySegment segment = null; + * try (Arena arena = Arena.ofConfined()) { + * segment = arena.allocate(100); * ... - * } // memory released here + * } // segment region deallocated here + * segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException *} * + * Memory segments allocated with a {@linkplain #ofConfined() confined arena} can only be accessed (and closed) by the + * thread that created the arena. If access to a memory segment from multiple threads is required, clients can allocate + * segments in a {@linkplain #ofShared() shared arena} instead. + *

+ * The characteristics of the various arenas are summarized in the following table: + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Arenas characteristics
KindBounded lifetimeExplicitly closeableAccessible from multiple threads
GlobalNoNoYes
AutomaticYesNoYes
ConfinedYesYesNo
SharedYesYesYes
+ * *

Safety and thread-confinement

* * Arenas provide strong temporal safety guarantees: a memory segment allocated by an arena cannot be accessed * after the arena has been closed. The cost of providing this guarantee varies based on the * number of threads that have access to the memory segments allocated by the arena. For instance, if an arena - * is always created and closed by one thread, and the memory segments associated with the arena's scope are always + * is always created and closed by one thread, and the memory segments allocated by the arena are always * accessed by that same thread, then ensuring correctness is trivial. *

* Conversely, if an arena allocates segments that can be accessed by multiple threads, or if the arena can be closed @@ -70,34 +135,120 @@ import jdk.internal.javac.PreviewFeature; * impact, arenas are divided into thread-confined arenas, and shared arenas. *

* Confined arenas, support strong thread-confinement guarantees. Upon creation, they are assigned an - * {@linkplain #isCloseableBy(Thread) owner thread}, typically the thread which initiated the creation operation. - * The segments created by a confined arena can only be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} + * owner thread, typically the thread which initiated the creation operation. + * The segments created by a confined arena can only be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} * by the owner thread. Moreover, any attempt to close the confined arena from a thread other than the owner thread will * fail with {@link WrongThreadException}. *

* Shared arenas, on the other hand, have no owner thread. The segments created by a shared arena - * can be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. This might be useful when + * can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. This might be useful when * multiple threads need to access the same memory segment concurrently (e.g. in the case of parallel processing). - * Moreover, a shared arena {@linkplain #isCloseableBy(Thread) can be closed} by any thread. + * Moreover, a shared arena can be closed by any thread. + * + *

Custom arenas

+ * + * Clients can define custom arenas to implement more efficient allocation strategies, or to have better control over + * when (and by whom) an arena can be closed. As an example, the following code defines a slicing arena that behaves + * like a confined arena (i.e., single-threaded access), but internally uses a + * {@linkplain SegmentAllocator#slicingAllocator(MemorySegment) slicing allocator} to respond to allocation requests. + * When the slicing arena is closed, the underlying confined arena is also closed; this will invalidate all segments + * allocated with the slicing arena (since the scope of the slicing arena is the same as that of the underlying + * confined arena): + * + * {@snippet lang = java: + * class SlicingArena implements Arena { + * final Arena arena = Arena.ofConfined(); + * final SegmentAllocator slicingAllocator; + * + * SlicingArena(long size) { + * slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size)); + * } + * + * public void allocate(long byteSize, long byteAlignment) { + * return slicingAllocator.allocate(byteSize, byteAlignment); + * } + * + * public MemorySegment.Scope scope() { + * return arena.scope(); + * } + * + * public void close() { + * return arena.close(); + * } + * } + * } + * + * In other words, a slicing arena provides a vastly more efficient and scalable allocation strategy, while still retaining + * the timely deallocation guarantee provided by the underlying confined arena: + * + * {@snippet lang = java: + * try (Arena slicingArena = new SlicingArena(1000)) { + * for (int i = 0 ; i < 10 ; i++) { + * MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5); + * ... + * } + * } // all memory allocated is released here + * } + * + * @implSpec + * Implementations of this interface are thread-safe. + * + * @see MemorySegment * * @since 20 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public interface Arena extends SegmentAllocator, AutoCloseable { + /** + * Creates a new arena that is managed, automatically, by the garbage collector. + * Segments obtained with the returned arena can be + * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. + * Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}. + * + * @return a new arena that is managed, automatically, by the garbage collector. + */ + static Arena ofAuto() { + return MemorySessionImpl.createImplicit(CleanerFactory.cleaner()).asArena(); + } + + /** + * Obtains the global arena. Segments obtained with the global arena can be + * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. + * Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}. + * + * @return the global arena. + */ + static Arena global() { + class Holder { + static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena(); + } + return Holder.GLOBAL; + } + + /** + * {@return a new confined arena, owned by the current thread} + */ + static Arena ofConfined() { + return MemorySessionImpl.createConfined(Thread.currentThread()).asArena(); + } + + /** + * {@return a new shared arena} + */ + static Arena ofShared() { + return MemorySessionImpl.createShared().asArena(); + } + /** * Returns a native memory segment with the given size (in bytes) and alignment constraint (in bytes). - * The returned segment is associated with the arena scope. + * The returned segment is associated with this {@linkplain #scope() arena scope}. * The segment's {@link MemorySegment#address() address} is the starting address of the * allocated off-heap memory region backing the segment, and the address is * aligned according the provided alignment constraint. * * @implSpec - * The default implementation of this method is equivalent to the following code: - * {@snippet lang = java: - * MemorySegment.allocateNative(bytesSize, byteAlignment, scope()); - *} - * More generally implementations of this method must return a native segment featuring the requested size, + * Implementations of this method must return a native segment featuring the requested size, * and that is compatible with the provided alignment constraint. Furthermore, for any two segments * {@code S1, S2} returned by this method, the following invariant must hold: * @@ -110,57 +261,43 @@ public interface Arena extends SegmentAllocator, AutoCloseable { * @return a new native memory segment. * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes} * is not a power of 2. - * @throws IllegalStateException if the arena has already been {@linkplain #close() closed}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. - * @see MemorySegment#allocateNative(long, long, SegmentScope) + * @throws IllegalStateException if this arena has already been {@linkplain #close() closed}. + * @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T} + * other than the arena owner thread. */ @Override default MemorySegment allocate(long byteSize, long byteAlignment) { - return MemorySegment.allocateNative(byteSize, byteAlignment, scope()); + return ((MemorySessionImpl)scope()).allocate(byteSize, byteAlignment); } /** * {@return the arena scope} */ - SegmentScope scope(); + Scope scope(); /** - * Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain SegmentScope#isAlive() alive}, + * Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain Scope#isAlive() alive}, * and all the memory segments associated with it can no longer be accessed. Furthermore, any off-heap region of memory backing the - * segments associated with that scope are also released. + * segments obtained from this arena are also released. * * @apiNote This operation is not idempotent; that is, closing an already closed arena always results in an * exception being thrown. This reflects a deliberate design choice: failure to close an arena might reveal a bug * in the underlying application logic. * - * @see SegmentScope#isAlive() + * @implSpec If this method completes normally, then {@code this.scope().isAlive() == false}. + * Implementations are allowed to throw {@link UnsupportedOperationException} if an explicit close operation is + * not supported. + * + * @see Scope#isAlive() * * @throws IllegalStateException if the arena has already been closed. - * @throws IllegalStateException if the arena scope is {@linkplain SegmentScope#whileAlive(Runnable) kept alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code isCloseableBy(T) == false}. + * @throws IllegalStateException if a segment associated with this arena is being accessed concurrently, e.g. + * by a {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}. + * @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T} + * other than the arena owner thread. + * @throws UnsupportedOperationException if this arena does not support explicit closure. */ @Override void close(); - /** - * {@return {@code true} if the provided thread can close this arena} - * @param thread the thread to be tested. - */ - boolean isCloseableBy(Thread thread); - - /** - * {@return a new confined arena, owned by the current thread} - */ - static Arena openConfined() { - return MemorySessionImpl.createConfined(Thread.currentThread()).asArena(); - } - - /** - * {@return a new shared arena} - */ - static Arena openShared() { - return MemorySessionImpl.createShared().asArena(); - } } diff --git a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java index 3b6627d7f92..27759a78845 100644 --- a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java +++ b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -37,7 +37,7 @@ import jdk.internal.javac.PreviewFeature; * A function descriptor models the signature of foreign functions. A function descriptor is made up of zero or more * argument layouts and zero or one return layout. A function descriptor is typically used when creating * {@linkplain Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) downcall method handles} or - * {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}. + * {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Linker.Option...) upcall stubs}. * * @implSpec * Implementing classes are immutable, thread-safe and value-based. diff --git a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java index 56ace4ee135..872f4923992 100644 --- a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -63,5 +63,14 @@ public sealed interface GroupLayout extends MemoryLayout permits StructLayout, U * {@inheritDoc} */ @Override + GroupLayout withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + * @throws IllegalArgumentException if {@code bitAlignment} is less than {@code M}, where {@code M} is the maximum alignment + * constraint in any of the member layouts associated with this group layout. + */ + @Override GroupLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 12ba7f6ce5f..3e6bfbb9e0f 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -34,8 +34,10 @@ import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import java.lang.invoke.MethodHandle; -import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -54,46 +56,340 @@ import java.util.stream.Stream; *
  • A linker allows Java code to link against foreign functions, via * {@linkplain #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handles}; and
  • *
  • A linker allows foreign functions to call Java method handles, - * via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}.
  • + * via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...) upcall stubs}. * * In addition, a linker provides a way to look up foreign functions in libraries that conform to the ABI. Each linker * chooses a set of libraries that are commonly used on the OS and processor combination associated with the ABI. * For example, a linker for Linux/x64 might choose two libraries: {@code libc} and {@code libm}. The functions in these * libraries are exposed via a {@linkplain #defaultLookup() symbol lookup}. - *

    - * The {@link #nativeLinker()} method provides a linker for the ABI associated with the OS and processor where the Java runtime - * is currently executing. This linker also provides access, via its {@linkplain #defaultLookup() default lookup}, - * to the native libraries loaded with the Java runtime. * - *

    Downcall method handles

    + *

    Calling native functions

    * - * {@linkplain #downcallHandle(FunctionDescriptor, Option...) Linking a foreign function} is a process which requires a function descriptor, - * a set of memory layouts which, together, specify the signature of the foreign function to be linked, and returns, - * when complete, a downcall method handle, that is, a method handle that can be used to invoke the target foreign function. - *

    - * The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is - * {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor. - * The downcall method handle type, might then be decorated by additional leading parameters, in the given order if both are present: - *

      - *
    • If the downcall method handle is created {@linkplain #downcallHandle(FunctionDescriptor, Option...) without specifying a target address}, - * the downcall method handle type features a leading parameter of type {@link MemorySegment}, from which the - * address of the target foreign function can be derived.
    • - *
    • If the function descriptor's return layout is a group layout, the resulting downcall method handle accepts - * an additional leading parameter of type {@link SegmentAllocator}, which is used by the linker runtime to allocate the - * memory region associated with the struct returned by the downcall method handle.
    • - *
    + * The {@linkplain #nativeLinker() native linker} can be used to link against functions + * defined in C libraries (native functions). Suppose we wish to downcall from Java to the {@code strlen} function + * defined in the standard C library: + * {@snippet lang = c: + * size_t strlen(const char *s); + * } + * A downcall method handle that exposes {@code strlen} is obtained, using the native linker, as follows: * - *

    Upcall stubs

    + * {@snippet lang = java: + * Linker linker = Linker.nativeLinker(); + * MethodHandle strlen = linker.downcallHandle( + * linker.defaultLookup().find("strlen").get(), + * FunctionDescriptor.of(JAVA_LONG, ADDRESS) + * ); + * } * - * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) Creating an upcall stub} requires a method - * handle and a function descriptor; in this case, the set of memory layouts in the function descriptor - * specify the signature of the function pointer associated with the upcall stub. + * Note how the native linker also provides access, via its {@linkplain #defaultLookup() default lookup}, + * to the native functions defined by the C libraries loaded with the Java runtime. Above, the default lookup + * is used to search the address of the {@code strlen} native function. That address is then passed, along with + * a platform-dependent description of the signature of the function expressed as a + * {@link FunctionDescriptor} (more on that below) to the native linker's + * {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...)} method. + * The obtained downcall method handle is then invoked as follows: + * + * {@snippet lang = java: + * try (Arena arena = Arena.openConfined()) { + * MemorySegment str = arena.allocateUtf8String("Hello"); + * long len = strlen.invoke(str); // 5 + * } + * } + *

    Describing C signatures

    + * + * When interacting with the native linker, clients must provide a platform-dependent description of the signature + * of the C function they wish to link against. This description, a {@link FunctionDescriptor function descriptor}, + * defines the layouts associated with the parameter types and return type (if any) of the C function. *

    - * The type of the provided method handle's type has to match the method type associated with the upcall stub, - * which is {@linkplain FunctionDescriptor#toMethodType() derived} from the provided function descriptor. + * Scalar C types such as {@code bool}, {@code int} are modelled as {@linkplain ValueLayout value layouts} + * of a suitable carrier. The mapping between a scalar type and its corresponding layout is dependent on the ABI + * implemented by the native linker. For instance, the C type {@code long} maps to the layout constant + * {@link ValueLayout#JAVA_LONG} on Linux/x64, but maps to the layout constant {@link ValueLayout#JAVA_INT} on + * Windows/x64. Similarly, the C type {@code size_t} maps to the layout constant {@link ValueLayout#JAVA_LONG} + * on 64-bit platforms, but maps to the layout constant {@link ValueLayout#JAVA_INT} on 32-bit platforms. *

    - * Upcall stubs are modelled by instances of type {@link MemorySegment}; upcall stubs can be passed by reference to other - * downcall method handles and, they are released via their associated {@linkplain SegmentScope scope}. + * Composite types are modelled as {@linkplain GroupLayout group layouts}. More specifically, a C {@code struct} type + * maps to a {@linkplain StructLayout struct layout}, whereas a C {@code union} type maps to a {@link UnionLayout union + * layout}. When defining a struct or union layout, clients must pay attention to the size and alignment constraint + * of the corresponding composite type definition in C. For instance, padding between two struct fields + * must be modelled explicitly, by adding an adequately sized {@linkplain PaddingLayout padding layout} member + * to the resulting struct layout. + *

    + * Finally, pointer types such as {@code int**} and {@code int(*)(size_t*, size_t*)} are modelled as + * {@linkplain AddressLayout address layouts}. When the spatial bounds of the pointer type are known statically, + * the address layout can be associated with a {@linkplain AddressLayout#targetLayout() target layout}. For instance, + * a pointer that is known to point to a C {@code int[2]} array can be modelled as an address layout whose + * target layout is a sequence layout whose element count is 2, and whose element type is {@link ValueLayout#JAVA_INT}. + *

    + * The following table shows some examples of how C types are modelled in Linux/x64: + * + *

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Mapping C types
    C typeLayoutJava type
    {@code bool}{@link ValueLayout#JAVA_BOOLEAN}{@code boolean}
    {@code char}{@link ValueLayout#JAVA_BYTE}{@code byte}
    {@code short}{@link ValueLayout#JAVA_SHORT}{@code short}
    {@code int}{@link ValueLayout#JAVA_INT}{@code int}
    {@code long}{@link ValueLayout#JAVA_LONG}{@code long}
    {@code long long}{@link ValueLayout#JAVA_LONG}{@code long}
    {@code float}{@link ValueLayout#JAVA_FLOAT}{@code float}
    {@code double}{@link ValueLayout#JAVA_DOUBLE}{@code double}
    {@code size_t}{@link ValueLayout#JAVA_LONG}{@code long}
    {@code char*}, {@code int**}, {@code struct Point*}{@link ValueLayout#ADDRESS}{@link MemorySegment}
    {@code int (*ptr)[10]} + *
    + * ValueLayout.ADDRESS.withTargetLayout(
    + *     MemoryLayout.sequenceLayout(10,
    + *         ValueLayout.JAVA_INT)
    + * );
    + * 
    + *
    {@link MemorySegment}
    struct Point { int x; long y; }; + *
    + * MemoryLayout.structLayout(
    + *     ValueLayout.JAVA_INT.withName("x"),
    + *     MemoryLayout.paddingLayout(32),
    + *     ValueLayout.JAVA_LONG.withName("y")
    + * );
    + * 
    + *
    {@link MemorySegment}
    union Choice { float a; int b; } + *
    + * MemoryLayout.unionLayout(
    + *     ValueLayout.JAVA_FLOAT.withName("a"),
    + *     ValueLayout.JAVA_INT.withName("b")
    + * );
    + * 
    + *
    {@link MemorySegment}
    + * + *

    Function pointers

    + * + * Sometimes, it is useful to pass Java code as a function pointer to some native function; this is achieved by using + * an {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...) upcall stub}. To demonstrate this, + * let's consider the following function from the C standard library: + * + * {@snippet lang = c: + * void qsort(void *base, size_t nmemb, size_t size, + * int (*compar)(const void *, const void *)); + * } + * + * The {@code qsort} function can be used to sort the contents of an array, using a custom comparator function which is + * passed as a function pointer (the {@code compar} parameter). To be able to call the {@code qsort} function from Java, + * we must first create a downcall method handle for it, as follows: + * + * {@snippet lang = java: + * Linker linker = Linker.nativeLinker(); + * MethodHandle qsort = linker.downcallHandle( + * linker.defaultLookup().find("qsort").get(), + * FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS) + * ); + * } + * + * As before, we use {@link ValueLayout#JAVA_LONG} to map the C type {@code size_t} type, and {@link ValueLayout#ADDRESS} + * for both the first pointer parameter (the array pointer) and the last parameter (the function pointer). + *

    + * To invoke the {@code qsort} downcall handle obtained above, we need a function pointer to be passed as the last + * parameter. That is, we need to create a function pointer out of an existing method handle. First, let's write a + * Java method that can compare two int elements passed as pointers (i.e. as {@linkplain MemorySegment memory segments}): + * + * {@snippet lang = java: + * class Qsort { + * static int qsortCompare(MemorySegment elem1, MemorySegmet elem2) { + * return Integer.compare(elem1.get(JAVA_INT, 0), elem2.get(JAVA_INT, 0)); + * } + * } + * } + * + * Now let's create a method handle for the comparator method defined above: + * + * {@snippet lang = java: + * FunctionDescriptor comparDesc = FunctionDescriptor.of(JAVA_INT, + * ADDRESS.withTargetLayout(JAVA_INT), + * ADDRESS.withTargetLayout(JAVA_INT)); + * MethodHandle comparHandle = MethodHandles.lookup() + * .findStatic(Qsort.class, "qsortCompare", + * comparDesc.toMethodType()); + * } + * + * First, we create a function descriptor for the function pointer type. Since we know that the parameters passed to + * the comparator method will be pointers to elements of a C {@code int[]} array, we can specify {@link ValueLayout#JAVA_INT} + * as the target layout for the address layouts of both parameters. This will allow the comparator method to access + * the contents of the array elements to be compared. We then {@linkplain FunctionDescriptor#toMethodType() turn} + * that function descriptor into a suitable {@linkplain java.lang.invoke.MethodType method type} which we then use to look up + * the comparator method handle. We can now create an upcall stub which points to that method, and pass it, as a function + * pointer, to the {@code qsort} downcall handle, as follows: + * + * {@snippet lang = java: + * try (Arena arena = Arena.ofConfined()) { + * MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena); + * MemorySegment array = session.allocateArray(0, 9, 3, 4, 6, 5, 1, 8, 2, 7); + * qsort.invokeExact(array, 10L, 4L, comparFunc); + * int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + * } + * } + * + * This code creates an off-heap array, copies the contents of a Java array into it, and then passes the array to the + * {@code qsort} method handle along with the comparator function we obtained from the native linker. After the invocation, the contents + * of the off-heap array will be sorted according to our comparator function, written in Java. We then extract a + * new Java array from the segment, which contains the sorted elements. + * + *

    Functions returning pointers

    + * + * When interacting with native functions, it is common for those functions to allocate a region of memory and return + * a pointer to that region. Let's consider the following function from the C standard library: + * + * {@snippet lang = c: + * void *malloc(size_t size); + * } + * + * The {@code malloc} function allocates a region of memory of given size, + * and returns a pointer to that region of memory, which is later deallocated using another function from + * the C standard library: + * + * {@snippet lang = c: + * void free(void *ptr); + * } + * + * The {@code free} function takes a pointer to a region of memory and deallocates that region. In this section we + * will show how to interact with these native functions, with the aim of providing a safe allocation + * API (the approach outlined below can of course be generalized to allocation functions other than {@code malloc} + * and {@code free}). + *

    + * First, we need to create the downcall method handles for {@code malloc} and {@code free}, as follows: + * + * {@snippet lang = java: + * Linker linker = Linker.nativeLinker(); + * + * MethodHandle malloc = linker.downcallHandle( + * linker.defaultLookup().find("malloc").get(), + * FunctionDescriptor.of(ADDRESS, JAVA_LONG) + * ); + * + * MethodHandle free = linker.downcallHandle( + * linker.defaultLookup().find("free").get(), + * FunctionDescriptor.ofVoid(ADDRESS) + * ); + * } + * + * When interacting with a native functions returning a pointer (such as {@code malloc}), the Java runtime has no insight + * into the size or the lifetime of the returned pointer. Consider the following code: + * + * {@snippet lang = java: + * MemorySegment segment = (MemorySegment)malloc.invokeExact(100); + * } + * + * The size of the segment returned by the {@code malloc} downcall method handle is + * zero. Moreover, the scope of the + * returned segment is a fresh scope that is always alive. To provide safe access to the segment, we must, + * unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to + * attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory + * backing the segment can be managed automatically, as for any other native segment created directly from Java code. + * Both these operations are accomplished using the restricted {@link MemorySegment#reinterpret(long, Arena, Consumer)} + * method, as follows: + * + * {@snippet lang = java: + * MemorySegment allocateMemory(long byteSize, Arena arena) { + * MemorySegment segment = (MemorySegment)malloc.invokeExact(byteSize); // size = 0, scope = always alive + * return segment.reinterpret(byteSize, arena, s -> free.invokeExact(s)); // size = byteSize, scope = arena.scope() + * } + * } + * + * The {@code allocateMemory} method defined above accepts two parameters: a size and an arena. The method calls the + * {@code malloc} downcall method handle, and unsafely reinterprets the returned segment, by giving it a new size + * (the size passed to the {@code allocateMemory} method) and a new scope (the scope of the provided arena). + * The method also specifies a cleanup action to be executed when the provided arena is closed. Unsurprisingly, + * the cleanup action passes the segment to the {@code free} downcall method handle, to deallocate the underlying + * region of memory. We can use the {@code allocateMemory} method as follows: + * + * {@snippet lang = java: + * try (Arena arena = Arena.ofConfined()) { + * MemorySegment segment = allocateMemory(100, arena); + * } // 'free' called here + * } + * + * Note how the segment obtained from {@code allocateMemory} acts as any other segment managed by the confined arena. More + * specifically, the obtained segment has the desired size, can only be accessed by a single thread (the thread which created + * the confined arena), and its lifetime is tied to the surrounding try-with-resources block. + * + *

    Variadic functions

    + * + * Variadic functions (e.g. a C function declared with a trailing ellipses {@code ...} at the end of the formal parameter + * list or with an empty formal parameter list) are not supported directly by the native linker. However, it is still possible + * to link a variadic function by using a specialized function descriptor, together with a + * {@linkplain Linker.Option#firstVariadicArg(int) a linker option} which indicates the position of the first variadic argument + * in that specialized descriptor. + *

    + * A well-known variadic function is the {@code printf} function, defined in the C standard library: + * + * {@snippet lang = c: + * int printf(const char *format, ...); + * } + * + * This function takes a format string, and a number of additional arguments (the number of such arguments is + * dictated by the format string). Consider the following variadic call: + * + * {@snippet lang = c: + * printf("%d plus %d equals %d", 2, 2, 4); + * } + * + * To perform an equivalent call using a downcall method handle we must create a function descriptor which + * describes the specialized signature of the C function we want to call. This descriptor must include layouts for any + * additional variadic argument we intend to provide. In this case, the specialized signature of the C + * function is {@code (char*, int, int, int)} as the format string accepts three integer parameters. Then, we need to use + * a linker option to specify the position of the first variadic layout in the provided function descriptor (starting from 0). + * In this case, since the first parameter is the format string (a non-variadic argument), the first variadic index + * needs to be set to 1, as follows: + * + * {@snippet lang = java: + * Linker linker = Linker.nativeLinker(); + * MethodHandle printf = linker.downcallHandle( + * linker.defaultLookup().lookup("printf").get(), + * FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT, JAVA_INT), + * Linker.Option.firstVariadicArg(1) // first int is variadic + * ); + * } + * + * We can then call the specialized downcall handle as usual: + * + * {@snippet lang = java: + * try (Arena arena = Arena.ofConfined()) { + * int res = (int)printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4" + * } + * } * *

    Safety considerations

    * @@ -101,21 +397,7 @@ import java.util.stream.Stream; * contain enough signature information (e.g. arity and types of foreign function parameters). As a consequence, * the linker runtime cannot validate linkage requests. When a client interacts with a downcall method handle obtained * through an invalid linkage request (e.g. by specifying a function descriptor featuring too many argument layouts), - * the result of such interaction is unspecified and can lead to JVM crashes. On downcall handle invocation, - * the linker runtime guarantees the following for any argument {@code A} of type {@link MemorySegment} whose corresponding - * layout is {@link ValueLayout#ADDRESS}: - *
      - *
    • The scope of {@code A} is {@linkplain SegmentScope#isAlive() alive}. Otherwise, the invocation throws - * {@link IllegalStateException};
    • - *
    • The invocation occurs in a thread {@code T} such that {@code A.scope().isAccessibleBy(T) == true}. - * Otherwise, the invocation throws {@link WrongThreadException}; and
    • - *
    • The scope of {@code A} is {@linkplain SegmentScope#whileAlive(Runnable) kept alive} during the invocation.
    • - *
    - * A downcall method handle created from a function descriptor whose return layout is an - * {@linkplain ValueLayout.OfAddress address layout} returns a native segment associated with - * the {@linkplain SegmentScope#global() global scope}. Under normal conditions, the size of the returned segment is {@code 0}. - * However, if the return layout is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, - * then the size of the returned segment is {@code Long.MAX_VALUE}. + * the result of such interaction is unspecified and can lead to JVM crashes. *

    * When creating upcall stubs the linker runtime validates the type of the target method handle against the provided * function descriptor and report an error if any mismatch is detected. As for downcalls, JVM crashes might occur, @@ -124,12 +406,6 @@ import java.util.stream.Stream; * handle associated with an upcall stub returns a {@linkplain MemorySegment memory segment}, clients must ensure * that this address cannot become invalid after the upcall completes. This can lead to unspecified behavior, * and even JVM crashes, since an upcall is typically executed in the context of a downcall method handle invocation. - *

    - * An upcall stub argument whose corresponding layout is an {@linkplain ValueLayout.OfAddress address layout} - * is a native segment associated with the {@linkplain SegmentScope#global() global scope}. - * Under normal conditions, the size of this segment argument is {@code 0}. However, if the layout associated with - * the upcall stub argument is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, - * then the size of the segment argument is {@code Long.MAX_VALUE}. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. @@ -143,31 +419,6 @@ public sealed interface Linker permits AbstractLinker { * Returns a linker for the ABI associated with the underlying native platform. The underlying native platform * is the combination of OS and processor where the Java runtime is currently executing. *

    - * When interacting with the returned linker, clients must describe the signature of a foreign function using a - * {@link FunctionDescriptor function descriptor} whose argument and return layouts are specified as follows: - *

      - *
    • Scalar types are modelled by a {@linkplain ValueLayout value layout} instance of a suitable carrier. Example - * of scalar types in C are {@code int}, {@code long}, {@code size_t}, etc. The mapping between a scalar type - * and its corresponding layout is dependent on the ABI of the returned linker; - *
    • Composite types are modelled by a {@linkplain GroupLayout group layout}. Depending on the ABI of the - * returned linker, additional {@linkplain MemoryLayout#paddingLayout(long) padding} member layouts might be required to conform - * to the size and alignment constraint of a composite type definition in C (e.g. using {@code struct} or {@code union}); and
    • - *
    • Pointer types are modelled by a {@linkplain ValueLayout value layout} instance with carrier {@link MemorySegment}. - * Examples of pointer types in C are {@code int**} and {@code int(*)(size_t*, size_t*)};
    • - *
    - *

    - * Any layout not listed above is unsupported; function descriptors containing unsupported layouts - * will cause an {@link IllegalArgumentException} to be thrown, when used to create a - * {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handle} or an - * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stub}. - *

    - * Variadic functions (e.g. a C function declared with a trailing ellipses {@code ...} at the end of the formal parameter - * list or with an empty formal parameter list) are not supported directly. However, it is possible to link a - * variadic function by using {@linkplain Linker.Option#firstVariadicArg(int) a linker option} to indicate - * the start of the list of variadic arguments, together with a specialized function descriptor describing a - * given variable arity callsite. Alternatively, where the foreign library allows it, clients might be able to - * interact with variadic functions by passing a trailing parameter of type {@link VaList} (e.g. as in {@code vsprintf}). - *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on @@ -178,7 +429,7 @@ public sealed interface Linker permits AbstractLinker { * linker are the native libraries loaded in the process where the Java runtime is currently executing. For example, * on Linux, these libraries typically include {@code libc}, {@code libm} and {@code libdl}. * - * @return a linker for the ABI associated with the OS and processor where the Java runtime is currently executing. + * @return a linker for the ABI associated with the underlying native platform. * @throws UnsupportedOperationException if the underlying native platform is not supported. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. */ @@ -189,11 +440,7 @@ public sealed interface Linker permits AbstractLinker { } /** - * Creates a method handle which can be used to call a foreign function with the given signature and address. - *

    - * If the provided method type's return type is {@code MemorySegment}, then the resulting method handle features - * an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker to allocate - * structs returned by-value. + * Creates a method handle which is used to call a foreign function with the given signature and address. *

    * Calling this method is equivalent to the following code: * {@snippet lang=java : @@ -214,17 +461,35 @@ public sealed interface Linker permits AbstractLinker { } /** - * Creates a method handle which can be used to call a foreign function with the given signature. - * The resulting method handle features a prefix parameter (as the first parameter) corresponding to the foreign function - * entry point, of type {@link MemorySegment}, which is used to specify the address of the target function - * to be called. + * Creates a method handle which is used to call a foreign function with the given signature. *

    - * If the provided function descriptor's return layout is a {@link GroupLayout}, then the resulting method handle features an - * additional prefix parameter (inserted immediately after the address parameter), of type {@link SegmentAllocator}), - * which will be used by the linker to allocate structs returned by-value. + * The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is + * {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor, + * but features an additional leading parameter of type {@link MemorySegment}, from which the address of the target + * foreign function is derived. Moreover, if the function descriptor's return layout is a group layout, the resulting + * downcall method handle accepts an additional leading parameter of type {@link SegmentAllocator}, which is used by + * the linker runtime to allocate the memory region associated with the struct returned by the downcall method handle. *

    - * The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} parameter passed to it is - * associated with the {@link MemorySegment#NULL} address, or a {@link NullPointerException} if that parameter is {@code null}. + * Upon invoking a downcall method handle, the linker runtime will guarantee the following for any argument + * {@code A} of type {@link MemorySegment} whose corresponding layout is an {@linkplain AddressLayout address layout}: + *

      + *
    • {@code A.scope().isAlive() == true}. Otherwise, the invocation throws {@link IllegalStateException};
    • + *
    • The invocation occurs in a thread {@code T} such that {@code A.isAccessibleBy(T) == true}. + * Otherwise, the invocation throws {@link WrongThreadException}; and
    • + *
    • {@code A} is kept alive during the invocation. For instance, if {@code A} has been obtained using a + * {@linkplain Arena#ofShared()} shared arena}, any attempt to {@linkplain Arena#close() close} + * the shared arena while the downcall method handle is executing will result in an {@link IllegalStateException}.
    • + *
    + *

    + * Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout}, + * invoking the returned method handle will return a native segment associated with + * a fresh scope that is always alive. Under normal conditions, the size of the returned segment is {@code 0}. + * However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout()} {@code T}, + * then the size of the returned segment is set to {@code T.byteSize()}. + *

    + * The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} + * representing the target address of the foreign function is the {@link MemorySegment#NULL} address. + * The returned method handle will additionally throw {@link NullPointerException} if any argument passed to it is {@code null}. * * @param function the function descriptor of the target function. * @param options any linker options. @@ -237,12 +502,19 @@ public sealed interface Linker permits AbstractLinker { /** * Creates a stub which can be passed to other foreign functions as a function pointer, associated with the given - * scope. Calling such a function pointer from foreign code will result in the execution of the provided + * arena. Calling such a function pointer from foreign code will result in the execution of the provided * method handle. *

    * The returned memory segment's address points to the newly allocated upcall stub, and is associated with - * the provided scope. As such, the corresponding upcall stub will be deallocated - * when the scope becomes not {@linkplain SegmentScope#isAlive() alive}. + * the provided arena. As such, the lifetime of the returned upcall stub segment is controlled by the + * provided arena. For instance, if the provided arena is a confined arena, the returned + * upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}. + *

    + * An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout} + * is a native segment associated with a fresh scope that is always alive. + * Under normal conditions, the size of this segment argument is {@code 0}. + * However, if the address layout has a {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the + * segment argument is set to {@code T.byteSize()}. *

    * The target method handle should not throw any exceptions. If the target method handle does throw an exception, * the VM will exit with a non-zero exit code. To avoid the VM aborting due to an uncaught exception, clients @@ -252,16 +524,17 @@ public sealed interface Linker permits AbstractLinker { * * @param target the target method handle. * @param function the upcall stub function descriptor. - * @param scope the scope associated with the returned upcall stub segment. + * @param arena the arena associated with the returned upcall stub segment. + * @param options any linker options. * @return a zero-length segment whose address is the address of the upcall stub. * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. * @throws IllegalArgumentException if it is determined that the target method handle can throw an exception, or if the target method handle * has a type that does not match the upcall stub inferred type. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. + * @throws IllegalStateException if {@code arena.scope().isAlive() == false} + * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a + * thread {@code T}, other than the arena's owner thread. */ - MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope); + MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options); /** * Returns a symbol lookup for symbols in a set of commonly used libraries. @@ -285,8 +558,7 @@ public sealed interface Linker permits AbstractLinker { */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) sealed interface Option - permits LinkerOptions.LinkerOptionImpl, - Option.CaptureCallState { + permits LinkerOptions.LinkerOptionImpl { /** * {@return a linker option used to denote the index of the first variadic argument layout in a @@ -302,70 +574,91 @@ public sealed interface Linker permits AbstractLinker { * calling a foreign function associated with a downcall method handle, * before it can be overwritten by the Java runtime, or read through conventional means} *

    - * A downcall method handle linked with this option will feature an additional {@link MemorySegment} - * parameter directly following the target address, and optional {@link SegmentAllocator} parameters. - * This memory segment must be a native segment into which the captured state is written. - * - * @param capturedState the names of the values to save. - * @see CaptureCallState#supported() - */ - static CaptureCallState captureCallState(String... capturedState) { - Set set = Stream.of(capturedState) - .map(CapturableState::forName) - .collect(Collectors.toSet()); - return new LinkerOptions.CaptureCallStateImpl(set); - } - - /** - * A linker option for saving portions of the execution state immediately - * after calling a foreign function associated with a downcall method handle, - * before it can be overwritten by the runtime, or read through conventional means. - *

    * Execution state is captured by a downcall method handle on invocation, by writing it * to a native segment provided by the user to the downcall method handle. - * For this purpose, a downcall method handle linked with the {@link #captureCallState(String[])} + * For this purpose, a downcall method handle linked with this * option will feature an additional {@link MemorySegment} parameter directly * following the target address, and optional {@link SegmentAllocator} parameters. - * This parameter represents the native segment into which the captured state is written. + * This parameter, called the 'capture state segment', represents the native segment into which + * the captured state is written. *

    - * The native segment should have the layout {@linkplain CaptureCallState#layout associated} - * with the particular {@code CaptureCallState} instance used to link the downcall handle. + * The capture state segment should have the layout returned by {@linkplain #captureStateLayout}. + * This layout is a struct layout which has a named field for each captured value. *

    - * Captured state can be retrieved from this native segment by constructing var handles - * from the {@linkplain #layout layout} associated with the {@code CaptureCallState} instance. + * Captured state can be retrieved from the capture state segment by constructing var handles + * from the {@linkplain #captureStateLayout capture state layout}. *

    * The following example demonstrates the use of this linker option: * {@snippet lang = "java": * MemorySegment targetAddress = ... - * CaptureCallState ccs = Linker.Option.captureCallState("errno"); + * Linker.Option ccs = Linker.Option.captureCallState("errno"); * MethodHandle handle = Linker.nativeLinker().downcallHandle(targetAddress, FunctionDescriptor.ofVoid(), ccs); * - * VarHandle errnoHandle = ccs.layout().varHandle(PathElement.groupElement("errno")); - * try (Arena arena = Arena.openConfined()) { - * MemorySegment capturedState = arena.allocate(ccs.layout()); + * StructLayout capturedStateLayout = Linker.Option.capturedStateLayout(); + * VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno")); + * try (Arena arena = Arena.ofConfined()) { + * MemorySegment capturedState = arena.allocate(capturedStateLayout); * handle.invoke(capturedState); * int errno = errnoHandle.get(capturedState); * // use errno * } * } + * + * @param capturedState the names of the values to save. + * @throws IllegalArgumentException if at least one of the provided {@code capturedState} names + * is unsupported on the current platform. + * @see #captureStateLayout() */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) - sealed interface CaptureCallState extends Option - permits LinkerOptions.CaptureCallStateImpl { - /** - * {@return A struct layout that represents the layout of the native segment passed - * to a downcall handle linked with this {@code CapturedCallState} instance} - */ - StructLayout layout(); + static Option captureCallState(String... capturedState) { + Set set = Stream.of(Objects.requireNonNull(capturedState)) + .map(Objects::requireNonNull) + .map(CapturableState::forName) + .collect(Collectors.toSet()); + return new LinkerOptions.CaptureCallState(set); + } - /** - * {@return the names of the state that can be capture by this implementation} - */ - static Set supported() { - return Arrays.stream(CapturableState.values()) - .map(CapturableState::stateName) - .collect(Collectors.toSet()); - } + /** + * {@return A struct layout that represents the layout of the capture state segment that is passed + * to a downcall handle linked with {@link #captureCallState(String...)}}. + *

    + * The capture state layout is platform dependent but is guaranteed to be + * a {@linkplain StructLayout struct layout} containing only {@linkplain ValueLayout value layouts} + * and possibly {@linkplain PaddingLayout padding layouts}. + * As an example, on Windows, the returned layout might contain three value layouts named: + *

      + *
    • GetLastError
    • + *
    • WSAGetLastError
    • + *
    • errno
    • + *
    + * The following snipet shows how to obtain the names of the supported captured value layouts: + * {@snippet lang = java: + * String capturedNames = Linker.Option.captureStateLayout().memberLayouts().stream() + * .map(MemoryLayout::name) + * .flatMap(Optional::stream) + * .map(Objects::toString) + * .collect(Collectors.joining(", ")); + * } + * + * @see #captureCallState(String...) + */ + static StructLayout captureStateLayout() { + return CapturableState.LAYOUT; + } + + /** + * {@return A linker option used to mark a foreign function as trivial} + *

    + * A trivial function is a function that has an extremely short running time + * in all cases (similar to calling an empty function), and does not call back into Java (e.g. using an upcall stub). + *

    + * Using this linker option is a hint which some implementations may use to apply + * optimizations that are only valid for trivial functions. + *

    + * Using this linker option when linking non trivial functions is likely to have adverse effects, + * such as loss of performance, or JVM crashes. + */ + static Option isTrivial() { + return LinkerOptions.IsTrivial.INSTANCE; } } } diff --git a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java index 8d56202a04a..ecef9f55a82 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -28,7 +28,6 @@ package java.lang.foreign; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; import java.util.EnumSet; import java.util.Objects; import java.util.Optional; @@ -36,6 +35,7 @@ import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Stream; + import jdk.internal.foreign.LayoutPath; import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind; import jdk.internal.foreign.Utils; @@ -44,7 +44,6 @@ import jdk.internal.foreign.layout.PaddingLayoutImpl; import jdk.internal.foreign.layout.SequenceLayoutImpl; import jdk.internal.foreign.layout.StructLayoutImpl; import jdk.internal.foreign.layout.UnionLayoutImpl; -import jdk.internal.foreign.layout.ValueLayouts; import jdk.internal.javac.PreviewFeature; /** @@ -200,6 +199,17 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin */ MemoryLayout withName(String name); + /** + * Returns a memory layout of the same type with the same size and alignment constraint as this layout, + * but without a name. + *

    + * This can be useful to compare two layouts that have different names, but are otherwise equal. + * + * @return a memory layout without a name. + * @see MemoryLayout#name() + */ + MemoryLayout withoutName(); + /** * Returns the alignment constraint associated with this layout, expressed in bits. Layout alignment defines a power * of two {@code A} which is the bit-wise alignment of the layout. If {@code A <= 8} then {@code A/8} is the number of @@ -235,10 +245,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @return the layout alignment constraint, in bytes. * @throws UnsupportedOperationException if {@code bitAlignment()} is not a multiple of 8. */ - default long byteAlignment() { - return Utils.bitsToBytesOrThrow(bitAlignment(), - () -> new UnsupportedOperationException("Cannot compute byte alignment; bit alignment is not a multiple of 8")); - } + long byteAlignment(); /** * Returns a memory layout of the same type with the same size and name as this layout, @@ -259,12 +266,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the * layout path contains one or more path elements that select multiple sequence element indices * (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}). + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements + * (see {@link PathElement#dereferenceElement()}). * @throws NullPointerException if either {@code elements == null}, or if any of the elements * in {@code elements} is {@code null}. */ default long bitOffset(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::offset, - EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements); + EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); } /** @@ -293,10 +302,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * specified by the given layout path elements, when supplied with the missing sequence element indices. * @throws IllegalArgumentException if the layout path contains one or more path elements that select * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}). + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements + * (see {@link PathElement#dereferenceElement()}). */ default MethodHandle bitOffsetHandle(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::offsetHandle, - EnumSet.of(PathKind.SEQUENCE_RANGE), elements); + EnumSet.of(PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); } /** @@ -308,12 +319,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the * layout path contains one or more path elements that select multiple sequence element indices * (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}). + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements + * (see {@link PathElement#dereferenceElement()}). * @throws UnsupportedOperationException if {@code bitOffset(elements)} is not a multiple of 8. * @throws NullPointerException if either {@code elements == null}, or if any of the elements * in {@code elements} is {@code null}. */ default long byteOffset(PathElement... elements) { - return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.BITS_TO_BYTES_THROW_OFFSET); + return Utils.bitsToBytes(bitOffset(elements)); } /** @@ -346,10 +359,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * specified by the given layout path elements, when supplied with the missing sequence element indices. * @throws IllegalArgumentException if the layout path contains one or more path elements that select * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}). + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements + * (see {@link PathElement#dereferenceElement()}). */ default MethodHandle byteOffsetHandle(PathElement... elements) { MethodHandle mh = bitOffsetHandle(elements); - mh = MethodHandles.filterReturnValue(mh, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET); + mh = MethodHandles.filterReturnValue(mh, Utils.BITS_TO_BYTES); return mh; } @@ -379,6 +394,28 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin *

    * Additionally, the provided dynamic values must conform to some bound which is derived from the layout path, that is, * {@code 0 <= x_i < b_i}, where {@code 1 <= i <= n}, or {@link IndexOutOfBoundsException} is thrown. + *

    + * Multiple paths can be chained, by using {@linkplain PathElement#dereferenceElement() dereference path elements}. + * A dereference path element allows to obtain a native memory segment whose base address is the address obtained + * by following the layout path elements immediately preceding the dereference path element. In other words, + * if a layout path contains one or more dereference path elements, the final address accessed by the returned + * var handle can be computed as follows: + * + *

    {@code
    +     * address_1 = base(segment) + offset_1
    +     * address_2 = base(segment_1) + offset_2
    +     * ...
    +     * address_k = base(segment_k-1) + offset_k
    +     * }
    + * + * where {@code k} is the number of dereference path elements in a layout path, {@code segment} is the input segment, + * {@code segment_1}, ... {@code segment_k-1} are the segments obtained by dereferencing the address associated with + * a given dereference path element (e.g. {@code segment_1} is a native segment whose base address is {@code address_1}), + * and {@code offset_1}, {@code offset_2}, ... {@code offset_k} are the offsets computed by evaluating + * the path elements after a given dereference operation (these offsets are obtained using the computation described + * above). In these more complex access operations, all memory accesses immediately preceding a dereference operation + * (e.g. those at addresses {@code address_1}, {@code address_2}, ..., {@code address_k-1} are performed using the + * {@link VarHandle.AccessMode#GET} access mode. * * @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every * unspecified sequence access component contained in this layout path. Moreover, the resulting var handle @@ -388,6 +425,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @return a var handle which can be used to access a memory segment at the (possibly nested) layout selected by the layout path in {@code elements}. * @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraint. * @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}). + * @throws IllegalArgumentException if the layout path in {@code elements} contains a {@linkplain PathElement#dereferenceElement() + * dereference path element} for an address layout that has no {@linkplain AddressLayout#targetLayout() target layout}. * @see MethodHandles#memorySegmentViewVarHandle(ValueLayout) */ default VarHandle varHandle(PathElement... elements) { @@ -432,6 +471,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @param elements the layout path elements. * @return a method handle which can be used to create a slice of the selected layout element, given a segment. * @throws UnsupportedOperationException if the size of the selected layout in bits is not a multiple of 8. + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements + * (see {@link PathElement#dereferenceElement()}). */ default MethodHandle sliceHandle(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::sliceHandle, @@ -446,10 +487,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, * or if the layout path contains one or more path elements that select one or more sequence element indices * (see {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}). + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements + * (see {@link PathElement#dereferenceElement()}). */ default MemoryLayout select(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::layout, - EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE), elements); + EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); } private static Z computePathOp(LayoutPath path, Function finalizer, @@ -489,6 +532,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * * @implSpec in case multiple group elements with a matching name exist, the path element returned by this * method will select the first one; that is, the group element with the lowest offset from current path is selected. + * In such cases, using {@link #groupElement(long)} might be preferable. * * @param name the name of the group element to be selected. * @return a path element which selects the group element with the given name. @@ -499,6 +543,23 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin path -> path.groupElement(name)); } + /** + * Returns a path element which selects a member layout with the given index in a group layout. + * The path element returned by this method does not alter the number of free dimensions of any path + * that is combined with such element. + * + * @param index the index of the group element to be selected. + * @return a path element which selects the group element with the given index. + * @throws IllegalArgumentException if {@code index < 0}. + */ + static PathElement groupElement(long index) { + if (index < 0) { + throw new IllegalArgumentException("Index < 0"); + } + return new LayoutPath.PathElementImpl(PathKind.GROUP_ELEMENT, + path -> path.groupElement(index)); + } + /** * Returns a path element which selects the element layout at the specified position in a sequence layout. * The path element returned by this method does not alter the number of free dimensions of any path @@ -578,6 +639,21 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin return new LayoutPath.PathElementImpl(PathKind.SEQUENCE_ELEMENT, LayoutPath::sequenceElement); } + + /** + * Returns a path element which dereferences an address layout as its + * {@linkplain AddressLayout#targetLayout() target layout} (where set). + * The path element returned by this method does not alter the number of free dimensions of any path + * that is combined with such element. Using this path layout to dereference an address layout + * that has no target layout results in an {@link IllegalArgumentException} (e.g. when + * a var handle is {@linkplain #varHandle(PathElement...) obtained}). + * + * @return a path element which dereferences an address layout. + */ + static PathElement dereferenceElement() { + return new LayoutPath.PathElementImpl(PathKind.DEREF_ELEMENT, + LayoutPath::derefElement); + } } /** @@ -611,60 +687,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin String toString(); /** - * Creates a padding layout with the given size. + * Creates a padding layout with the given bitSize and a bit-alignment of eight. * - * @param size the padding size in bits. + * @param bitSize the padding size in bits. * @return the new selector layout. - * @throws IllegalArgumentException if {@code size <= 0}. + * @throws IllegalArgumentException if {@code bitSize <= 0} or {@code bitSize % 8 != 0} */ - static PaddingLayout paddingLayout(long size) { - MemoryLayoutUtil.checkSize(size); - return PaddingLayoutImpl.of(size); - } - - /** - * Creates a value layout of given Java carrier and byte order. The type of resulting value layout is determined - * by the carrier provided: - *
      - *
    • {@link ValueLayout.OfBoolean}, for {@code boolean.class}
    • - *
    • {@link ValueLayout.OfByte}, for {@code byte.class}
    • - *
    • {@link ValueLayout.OfShort}, for {@code short.class}
    • - *
    • {@link ValueLayout.OfChar}, for {@code char.class}
    • - *
    • {@link ValueLayout.OfInt}, for {@code int.class}
    • - *
    • {@link ValueLayout.OfFloat}, for {@code float.class}
    • - *
    • {@link ValueLayout.OfLong}, for {@code long.class}
    • - *
    • {@link ValueLayout.OfDouble}, for {@code double.class}
    • - *
    • {@link ValueLayout.OfAddress}, for {@code MemorySegment.class}
    • - *
    - * @param carrier the value layout carrier. - * @param order the value layout's byte order. - * @return a value layout with the given Java carrier and byte-order. - * @throws IllegalArgumentException if the carrier type is not supported. - */ - static ValueLayout valueLayout(Class carrier, ByteOrder order) { - Objects.requireNonNull(carrier); - Objects.requireNonNull(order); - if (carrier == boolean.class) { - return ValueLayouts.OfBooleanImpl.of(order); - } else if (carrier == char.class) { - return ValueLayouts.OfCharImpl.of(order); - } else if (carrier == byte.class) { - return ValueLayouts.OfByteImpl.of(order); - } else if (carrier == short.class) { - return ValueLayouts.OfShortImpl.of(order); - } else if (carrier == int.class) { - return ValueLayouts.OfIntImpl.of(order); - } else if (carrier == float.class) { - return ValueLayouts.OfFloatImpl.of(order); - } else if (carrier == long.class) { - return ValueLayouts.OfLongImpl.of(order); - } else if (carrier == double.class) { - return ValueLayouts.OfDoubleImpl.of(order); - } else if (carrier == MemorySegment.class) { - return ValueLayouts.OfAddressImpl.of(order); - } else { - throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName()); - } + static PaddingLayout paddingLayout(long bitSize) { + return PaddingLayoutImpl.of(MemoryLayoutUtil.requireBitSizeValid(bitSize, false)); } /** @@ -674,10 +704,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @param elementLayout the sequence element layout. * @return the new sequence layout with the given element layout and size. * @throws IllegalArgumentException if {@code elementCount } is negative. + * @throws IllegalArgumentException if {@code elementLayout.bitAlignment() > elementLayout.bitSize()}. */ static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) { - MemoryLayoutUtil.checkSize(elementCount, true); + MemoryLayoutUtil.requireNonNegative(elementCount); Objects.requireNonNull(elementLayout); + Utils.checkElementAlignment(elementLayout, "Element layout alignment greater than its size"); return wrapOverflow(() -> SequenceLayoutImpl.of(elementCount, elementLayout)); } @@ -693,6 +725,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * * @param elementLayout the sequence element layout. * @return a new sequence layout with the given element layout and maximum element count. + * @throws IllegalArgumentException if {@code elementLayout.bitAlignment() > elementLayout.bitSize()}. */ static SequenceLayout sequenceLayout(MemoryLayout elementLayout) { Objects.requireNonNull(elementLayout); diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 11633df2ce7..1580787431f 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -27,6 +27,8 @@ package java.lang.foreign; import java.io.UncheckedIOException; +import java.lang.foreign.Linker.Option; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -39,17 +41,17 @@ import java.util.Arrays; import java.util.Objects; import java.util.Optional; import java.util.Spliterator; +import java.util.function.Consumer; import java.util.stream.Stream; import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.HeapMemorySegmentImpl; +import jdk.internal.foreign.MemorySessionImpl; import jdk.internal.foreign.NativeMemorySegmentImpl; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.layout.ValueLayouts; import jdk.internal.javac.PreviewFeature; -import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.reflect.CallerSensitive; -import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; /** @@ -63,10 +65,10 @@ import jdk.internal.vm.annotation.ForceInline; * Heap segments can be obtained by calling one of the {@link MemorySegment#ofArray(int[])} factory methods. * These methods return a memory segment backed by the on-heap region that holds the specified Java array. *

    - * Native segments can be obtained by calling one of the {@link MemorySegment#allocateNative(long, long, SegmentScope)} + * Native segments can be obtained by calling one of the {@link Arena#allocate(long, long)} * factory methods, which return a memory segment backed by a newly allocated off-heap region with the given size * and aligned to the given alignment constraint. Alternatively, native segments can be obtained by - * {@link FileChannel#map(MapMode, long, long, SegmentScope) mapping} a file into a new off-heap region + * {@link FileChannel#map(MapMode, long, long, Arena) mapping} a file into a new off-heap region * (in some systems, this operation is sometimes referred to as {@code mmap}). * Segments obtained in this way are called mapped segments, and their contents can be {@linkplain #force() persisted} and * {@linkplain #load() loaded} to and from the underlying memory-mapped file. @@ -91,23 +93,22 @@ import jdk.internal.vm.annotation.ForceInline; * Every memory segment has a {@linkplain #byteSize() size}. The size of a heap segment is derived from the Java array * from which it is obtained. This size is predictable across Java runtimes. * The size of a native segment is either passed explicitly - * (as in {@link MemorySegment#allocateNative(long, SegmentScope)}) or derived from a {@link MemoryLayout} - * (as in {@link MemorySegment#allocateNative(MemoryLayout, SegmentScope)}). The size of a memory segment is typically + * (as in {@link Arena#allocate(long, long)}) or derived from a {@link MemoryLayout} + * (as in {@link Arena#allocate(MemoryLayout)}). The size of a memory segment is typically * a positive number but may be zero, but never negative. *

    * The address and size of a memory segment jointly ensure that access operations on the segment cannot fall * outside the boundaries of the region of memory which backs the segment. * That is, a memory segment has spatial bounds. *

    - * Every memory segment is associated with a {@linkplain SegmentScope scope}. This ensures that access operations + * Every memory segment is associated with a {@linkplain Scope scope}. This ensures that access operations * on a memory segment cannot occur when the region of memory which backs the memory segment is no longer available - * (e.g., after the scope associated with the accessed memory segment is no longer {@linkplain SegmentScope#isAlive() alive}). + * (e.g., after the scope associated with the accessed memory segment is no longer {@linkplain Scope#isAlive() alive}). * That is, a memory segment has temporal bounds. *

    - * Finally, access operations on a memory segment are subject to the thread-confinement checks enforced by the associated - * scope; that is, if the segment is associated with the {@linkplain SegmentScope#global() global scope} or an {@linkplain SegmentScope#auto() automatic scope}, - * it can be accessed by multiple threads. If the segment is associated with an arena scope, then it can only be - * accessed compatibly with the arena confinement characteristics. + * Finally, access operations on a memory segment can be subject to additional thread-confinement checks. + * Heap segments can be accessed from any thread. Conversely, native segments can only be accessed compatibly with the + * confinement characteristics of the arena used to obtain them. * *

    Accessing memory segments

    * @@ -161,28 +162,28 @@ import jdk.internal.vm.annotation.ForceInline; * segment is derived from the address of the original segment, by adding an offset (expressed in bytes). The size of * the sliced segment is either derived implicitly (by subtracting the specified offset from the size of the original segment), * or provided explicitly. In other words, a sliced segment has stricter spatial bounds than those of the original segment: - * {@snippet lang=java : + * {@snippet lang = java: * Arena arena = ... * MemorySegment segment = arena.allocate(100); * MemorySegment slice = segment.asSlice(50, 10); * slice.get(ValueLayout.JAVA_INT, 20); // Out of bounds! * arena.close(); * slice.get(ValueLayout.JAVA_INT, 0); // Already closed! - * } + *} * The above code creates a native segment that is 100 bytes long; then, it creates a slice that starts at offset 50 * of {@code segment}, and is 10 bytes long. That is, the address of the {@code slice} is {@code segment.address() + 50}, * and its size is 10. As a result, attempting to read an int value at offset 20 of the - * {@code slice} segment will result in an exception. The {@linkplain SegmentScope temporal bounds} of the original segment - * is inherited by its slices; that is, when the scope associated with {@code segment} is no longer {@linkplain SegmentScope#isAlive() alive}, + * {@code slice} segment will result in an exception. The {@linkplain Arena temporal bounds} of the original segment + * is inherited by its slices; that is, when the scope associated with {@code segment} is no longer {@linkplain Scope#isAlive() alive}, * {@code slice} will also be become inaccessible. *

    * A client might obtain a {@link Stream} from a segment, which can then be used to slice the segment (according to a given * element layout) and even allow multiple threads to work in parallel on disjoint segment slices - * (to do this, the segment has to be associated with a scope that allows {@linkplain SegmentScope#isAccessibleBy(Thread) access} + * (to do this, the segment has to be {@linkplain MemorySegment#isAccessibleBy(Thread) accessible} * from multiple threads). The following code can be used to sum all int values in a memory segment in parallel: * * {@snippet lang = java: - * try (Arena arena = Arena.openShared()) { + * try (Arena arena = Arena.ofShared()) { * SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); * MemorySegment segment = arena.allocate(SEQUENCE_LAYOUT); * int sum = segment.elements(ValueLayout.JAVA_INT).parallel() @@ -241,8 +242,8 @@ import jdk.internal.vm.annotation.ForceInline; *

    * The alignment constraint used to access a segment is typically dictated by the shape of the data structure stored * in the segment. For example, if the programmer wishes to store a sequence of 8-byte values in a native segment, then - * the segment should be allocated by specifying a 8-byte alignment constraint, either via {@link #allocateNative(long, long, SegmentScope)} - * or {@link #allocateNative(MemoryLayout, SegmentScope)}. These factories ensure that the off-heap region of memory backing + * the segment should be allocated by specifying a 8-byte alignment constraint, either via {@link Arena#allocate(long, long)} + * or {@link Arena#allocate(MemoryLayout)}. These factories ensure that the off-heap region of memory backing * the returned segment has a starting address that is 8-byte aligned. Subsequently, the programmer can access the * segment at the offsets of interest -- 0, 8, 16, 24, etc -- in the knowledge that every such access is aligned. *

    @@ -343,53 +344,82 @@ import jdk.internal.vm.annotation.ForceInline; * the region, stored in the pointer, is available. For example, a C function with return type {@code char*} might return * a pointer to a region containing a single {@code char} value, or to a region containing an array of {@code char} values, * where the size of the array might be provided in a separate parameter. The size of the array is not readily apparent - * to the code calling the foreign function and hoping to use its result. + * to the code calling the foreign function and hoping to use its result. In addition to having no insight + * into the size of the region of memory backing a pointer returned from a foreign function, it also has no insight + * into the lifetime intended for said region of memory by the foreign function that allocated it. *

    - * The {@link Linker} represents a pointer returned from a foreign function with a zero-length memory segment. - * The address of the segment is the address stored in the pointer. The size of the segment is zero. Similarly, when a - * client reads an address from a memory segment, a zero-length memory segment is returned. + * The {@code MemorySegment} API uses zero-length memory segments to represent: + *

    + * The address of the zero-length segment is the address stored in the pointer. The spatial and temporal bounds of the + * zero-length segment are as follows: + *
      + *
    • The size of the segment is zero. any attempt to access these segments will fail with {@link IndexOutOfBoundsException}. + * This is a crucial safety feature: as these segments are associated with a region + * of memory whose size is not known, any access operations involving these segments cannot be validated. + * In effect, a zero-length memory segment wraps an address, and it cannot be used without explicit intent + * (see below);
    • + *
    • The segment is associated with a fresh scope that is always alive. Thus, while zero-length + * memory segments cannot be accessed directly, they can be passed, opaquely, to other pointer-accepting foreign functions.
    • + *
    *

    - * Since a zero-length segment features trivial spatial bounds, any attempt to access these segments will fail with - * {@link IndexOutOfBoundsException}. This is a crucial safety feature: as these segments are associated with a region - * of memory whose size is not known, any access operations involving these segments cannot be validated. - * In effect, a zero-length memory segment wraps an address, and it cannot be used without explicit intent. + * To demonstrate how clients can work with zero-length memory segments, consider the case of a client that wants + * to read a pointer from some memory segment. This can be done via the + * {@linkplain MemorySegment#get(AddressLayout, long)} access method. This method accepts an + * {@linkplain AddressLayout address layout} (e.g. {@link ValueLayout#ADDRESS}), the layout of the pointer + * to be read. For instance on a 64-bit platform, the size of an address layout is 64 bits. The access operation + * also accepts an offset, expressed in bytes, which indicates the position (relative to the start of the memory segment) + * at which the pointer is stored. The access operation returns a zero-length native memory segment, backed by a region + * of memory whose starting address is the 64-bit value read at the specified offset. *

    - * Zero-length memory segments obtained when interacting with foreign functions are associated with the - * {@link SegmentScope#global() global scope}. This is because the Java runtime, in addition to having no insight - * into the size of the region of memory backing a pointer returned from a foreign function, also has no insight - * into the lifetime intended for said region of memory by the foreign function that allocated it. The global scope - * ensures that the obtained segment can be passed, opaquely, to other pointer-accepting foreign functions. - *

    - * To access native zero-length memory segments, clients have two options, both of which are unsafe. Clients - * can {@linkplain java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope) obtain} - * a new native segment, with new spatial and temporal bounds, as follows: + * The returned zero-length memory segment cannot be accessed directly by the client: since the size of the segment + * is zero, any access operation would result in out-of-bounds access. Instead, the client must, unsafely, + * assign new spatial bounds to the zero-length memory segment. This can be done via the + * {@link #reinterpret(long)} method, as follows: * * {@snippet lang = java: - * SegmentScope scope = ... // obtains a scope - * MemorySegment foreign = someSegment.get(ValueLayout.ADDRESS, 0); // wrap address into segment (size = 0) - * MemorySegment segment = MemorySegment.ofAddress(foreign.address(), 4, scope); // create new segment (size = 4) - * int x = segment.get(ValueLayout.JAVA_INT, 0); //ok + * MemorySegment z = segment.get(ValueLayout.ADDRESS, ...); // size = 0 + * MemorySegment ptr = z.reinterpret(16); // size = 16 + * int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // ok *} - * - * Alternatively, clients can obtain an {@linkplain java.lang.foreign.ValueLayout.OfAddress#asUnbounded() unbounded} - * address value layout. When an access operation, or a function descriptor that is passed to a downcall method handle, - * uses an unbounded address value layouts, the runtime will wrap any corresponding raw addresses with native segments - * with maximal size (i.e. {@linkplain java.lang.Long#MAX_VALUE}). As such, these segments can be accessed directly, as follows: + *

    + * In some cases, the client might additionally want to assign new temporal bounds to a zero-length memory segment. + * This can be done via the {@link #reinterpret(long, Arena, Consumer)} method, which returns a + * new native segment with the desired size and the same temporal bounds as those of the provided arena: * * {@snippet lang = java: - * MemorySegment foreign = someSegment.get(ValueLayout.ADDRESS.asUnbounded(), 0); // wrap address into segment (size = Long.MAX_VALUE) - * int x = foreign.get(ValueLayout.JAVA_INT, 0); //ok + * MemorySegment ptr = null; + * try (Arena arena = Arena.ofConfined()) { + * MemorySegment z = segment.get(ValueLayout.ADDRESS, ...); // size = 0, scope = always alive + * ptr = z.reinterpret(16, arena, null); // size = 4, scope = arena.scope() + * int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // ok + * } + * int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // throws IllegalStateException *} * - * Both {@link #ofAddress(long, long, SegmentScope)} and {@link ValueLayout.OfAddress#asUnbounded()} are + * Alternatively, if the size of the region of memory backing the zero-length memory segment is known statically, + * the client can overlay a {@linkplain AddressLayout#withTargetLayout(MemoryLayout) target layout} on the address + * layout used when reading a pointer. The target layout is then used to dynamically + * expand the size of the native memory segment returned by the access operation, so that the size + * of the segment is the same as the size of the target layout. In other words, the returned segment is no + * longer a zero-length memory segment, and the pointer it represents can be dereferenced directly: + * + * {@snippet lang = java: + * AddressLayout intArrPtrLayout = ValueLayout.ADDRESS.withTargetLayout( + * MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT)); // layout for int (*ptr)[4] + * MemorySegment ptr = segment.get(intArrPtrLayout, ...); // size = 16 + * int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // ok + *} + *

    + * All the methods which can be used to manipulate zero-length memory segments + * ({@link #reinterpret(long)}, {@link #reinterpret(Arena, Consumer)}, {@link #reinterpret(long, Arena, Consumer)} and + * {@link AddressLayout#withTargetLayout(MemoryLayout)}) are * restricted methods, and should be used with caution: - * for instance, sizing a segment incorrectly could result in a VM crash when attempting to access the memory segment. - *

    - * Which approach is taken largely depends on the information that a client has available when obtaining a memory segment - * wrapping a native pointer. For instance, if such pointer points to a C struct, the client might prefer to resize the - * segment unsafely, to match the size of the struct (so that out-of-bounds access will be detected by the API). - * In other instances, however, there will be no, or little information as to what spatial and/or temporal bounds should - * be associated with a given native pointer. In these cases using an unbounded address layout might be preferable. + * assigning a segment incorrect spatial and/or temporal bounds could result in a VM crash when attempting to access + * the memory segment. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. @@ -405,9 +435,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { long address(); /** - * {@return the Java array associated with this memory segment, if any} + * Returns the Java object stored in the on-heap memory region backing this memory segment, if any. For instance, if this + * memory segment is a heap segment created with the {@link #ofArray(byte[])} factory method, this method will return the + * {@code byte[]} object which was used to obtain the segment. This method returns an empty {@code Optional} value + * if either this segment is a {@linkplain #isNative() native} segment, or if this segment is {@linkplain #isReadOnly() read-only}. + * @return the Java object associated with this memory segment, if any. */ - Optional array(); + Optional heapBase(); /** * Returns a spliterator for this memory segment. The returned spliterator reports {@link Spliterator#SIZED}, @@ -418,7 +452,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * if the supplied layout has size N, then calling {@link Spliterator#trySplit()} will result in a spliterator serving * approximately {@code S/N} elements (depending on whether N is even or not), where {@code S} is the size of * this segment. As such, splitting is possible as long as {@code S/N >= 2}. The spliterator returns segments that - * are associated with the same scope as that associated with this segment. + * have the same lifetime as that of this segment. *

    * The returned spliterator effectively allows to slice this segment into disjoint {@linkplain #asSlice(long, long) slices}, * which can then be processed in parallel by multiple threads. @@ -451,7 +485,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * {@return the scope associated with this memory segment} */ - SegmentScope scope(); + Scope scope(); + + /** + * {@return {@code true} if this segment can be accessed from the provided thread} + * @param thread the thread to be tested. + */ + boolean isAccessibleBy(Thread thread); /** * {@return the size (in bytes) of this memory segment} @@ -461,8 +501,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Returns a slice of this memory segment, at the given offset. The returned segment's address is the address * of this segment plus the given offset; its size is specified by the given argument. + *

    + * Equivalent to the following code: + * {@snippet lang=java : + * asSlice(offset, layout.byteSize(), 1); + * } * - * @see #asSlice(long) + * @see #asSlice(long, long, long) * * @param offset The new segment base offset (relative to the address of this segment), specified in bytes. * @param newSize The new segment size, specified in bytes. @@ -471,6 +516,44 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { */ MemorySegment asSlice(long offset, long newSize); + /** + * Returns a slice of this memory segment, at the given offset, with the provided alignment constraint. + * The returned segment's address is the address of this segment plus the given offset; its size is specified by the given argument. + * + * @param offset The new segment base offset (relative to the address of this segment), specified in bytes. + * @param newSize The new segment size, specified in bytes. + * @param byteAlignment The alignment constraint (in bytes) of the returned slice. + * @return a slice of this memory segment. + * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset} + * @throws IllegalArgumentException if this segment cannot be accessed at {@code offset} under + * the provided alignment constraint. + */ + MemorySegment asSlice(long offset, long newSize, long byteAlignment); + + /** + * Returns a slice of this memory segment with the given layout, at the given offset. The returned segment's address is the address + * of this segment plus the given offset; its size is the same as the size of the provided layout. + *

    + * Equivalent to the following code: + * {@snippet lang=java : + * asSlice(offset, layout.byteSize(), layout.byteAlignment()); + * } + * + * @see #asSlice(long, long, long) + * + * @param offset The new segment base offset (relative to the address of this segment), specified in bytes. + * @param layout The layout of the segment slice. + * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > layout.byteSize()}, + * {@code newSize < 0}, or {@code newSize > layout.byteSize() - offset} + * @throws IllegalArgumentException if this segment cannot be accessed at {@code offset} under + * the alignment constraint specified by {@code layout}. + * @return a slice of this memory segment. + */ + default MemorySegment asSlice(long offset, MemoryLayout layout) { + Objects.requireNonNull(layout); + return asSlice(offset, layout.byteSize(), layout.byteAlignment()); + } + /** * Returns a slice of this memory segment, at the given offset. The returned segment's address is the address * of this segment plus the given offset; its size is computed by subtracting the specified offset from this segment size. @@ -486,9 +569,110 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @return a slice of this memory segment. * @throws IndexOutOfBoundsException if {@code offset < 0}, or {@code offset > byteSize()}. */ - default MemorySegment asSlice(long offset) { - return asSlice(offset, byteSize() - offset); - } + MemorySegment asSlice(long offset); + + /** + * Returns a new memory segment that has the same address and scope as this segment, but with the provided size. + *

    + * This method is restricted. + * Restricted methods are unsafe, and, if used incorrectly, their use might crash + * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on + * restricted methods, and use safe and supported functionalities, where possible. + * + * @param newSize the size of the returned segment. + * @return a new memory segment that has the same address and scope as this segment, but the new + * provided size. + * @throws IllegalArgumentException if {@code newSize < 0}. + * @throws UnsupportedOperationException if this segment is not a {@linkplain #isNative() native} segment. + * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. + */ + @CallerSensitive + MemorySegment reinterpret(long newSize); + + /** + * Returns a new memory segment with the same address and size as this segment, but with the provided scope. + * As such, the returned segment cannot be accessed after the provided arena has been closed. + * Moreover, the returned segment can be accessed compatibly with the confinement restrictions associated with the + * provided arena: that is, if the provided arena is a {@linkplain Arena#ofConfined() confined arena}, + * the returned segment can only be accessed by the arena's owner thread, regardless of the confinement restrictions + * associated with this segment. In other words, this method returns a segment that behaves as if it had been allocated + * using the provided arena. + *

    + * Clients can specify an optional cleanup action that should be executed when the provided scope becomes + * invalid. This cleanup action receives a fresh memory segment that is obtained from this segment as follows: + * {@snippet lang=java : + * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()); + * } + * That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive, + * and is accessible from any thread. The size of the segment accepted by the cleanup action is {@link #byteSize()}. + *

    + * This method is restricted. + * Restricted methods are unsafe, and, if used incorrectly, their use might crash + * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on + * restricted methods, and use safe and supported functionalities, where possible. + * + * @apiNote The cleanup action (if present) should take care not to leak the received segment to external + * clients which might access the segment after its backing region of memory is no longer available. Furthermore, + * if the provided scope is the scope of an {@linkplain Arena#ofAuto() automatic arena}, the cleanup action + * must not prevent the scope from becoming unreachable. + * A failure to do so will permanently prevent the regions of memory allocated by the automatic arena from being deallocated. + *

    + * This method is restricted. + * Restricted methods are unsafe, and, if used incorrectly, their use might crash + * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on + * restricted methods, and use safe and supported functionalities, where possible. + * + * @param arena the arena to be associated with the returned segment. + * @param cleanup the cleanup action that should be executed when the provided arena is closed (can be {@code null}). + * @return a new memory segment with unbounded size. + * @throws IllegalArgumentException if {@code newSize < 0}. + * @throws IllegalStateException if {@code scope.isAlive() == false}. + * @throws UnsupportedOperationException if this segment is not a {@linkplain #isNative() native} segment. + * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. + */ + @CallerSensitive + MemorySegment reinterpret(Arena arena, Consumer cleanup); + + /** + * Returns a new segment with the same address as this segment, but with the provided size and scope. + * As such, the returned segment cannot be accessed after the provided arena has been closed. + * Moreover, if the returned segment can be accessed compatibly with the confinement restrictions associated with the + * provided arena: that is, if the provided arena is a {@linkplain Arena#ofConfined() confined arena}, + * the returned segment can only be accessed by the arena's owner thread, regardless of the confinement restrictions + * associated with this segment. In other words, this method returns a segment that behaves as if it had been allocated + * using the provided arena. + *

    + * Clients can specify an optional cleanup action that should be executed when the provided scope becomes + * invalid. This cleanup action receives a fresh memory segment that is obtained from this segment as follows: + * {@snippet lang=java : + * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()); + * } + * That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive, + * and is accessible from any thread. The size of the segment accepted by the cleanup action is {@code newSize}. + *

    + * This method is restricted. + * Restricted methods are unsafe, and, if used incorrectly, their use might crash + * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on + * restricted methods, and use safe and supported functionalities, where possible. + * + * @apiNote The cleanup action (if present) should take care not to leak the received segment to external + * clients which might access the segment after its backing region of memory is no longer available. Furthermore, + * if the provided scope is the scope of an {@linkplain Arena#ofAuto() automatic arena}, the cleanup action + * must not prevent the scope from becoming unreachable. + * A failure to do so will permanently prevent the regions of memory allocated by the automatic arena from being deallocated. + * + * @param newSize the size of the returned segment. + * @param arena the arena to be associated with the returned segment. + * @param cleanup the cleanup action that should be executed when the provided arena is closed (can be {@code null}). + * @return a new segment that has the same address as this segment, but with new size and its scope set to + * that of the provided arena. + * @throws UnsupportedOperationException if this segment is not a {@linkplain #isNative() native} segment. + * @throws IllegalArgumentException if {@code newSize < 0}. + * @throws IllegalStateException if {@code scope.isAlive() == false}. + * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. + */ + @CallerSensitive + MemorySegment reinterpret(long newSize, Arena arena, Consumer cleanup); /** * {@return {@code true}, if this segment is read-only} @@ -506,7 +690,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Returns {@code true} if this segment is a native segment. A native segment is - * created e.g. using the {@link #allocateNative(long, SegmentScope)} (and related) factory, or by + * created e.g. using the {@link Arena#allocate(long, long)} (and related) factory, or by * {@linkplain #ofBuffer(Buffer) wrapping} a {@linkplain ByteBuffer#allocateDirect(int) direct buffer}. * @return {@code true} if this segment is native segment. */ @@ -514,7 +698,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Returns {@code true} if this segment is a mapped segment. A mapped memory segment is created e.g. using the - * {@link FileChannel#map(FileChannel.MapMode, long, long, SegmentScope)} factory, or by + * {@link FileChannel#map(FileChannel.MapMode, long, long, Arena)} factory, or by * {@linkplain #ofBuffer(Buffer) wrapping} a {@linkplain java.nio.MappedByteBuffer mapped byte buffer}. * @return {@code true} if this segment is a mapped segment. */ @@ -581,9 +765,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param value the value to fill into this segment * @return this memory segment * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}). */ MemorySegment fill(byte value); @@ -600,13 +784,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param src the source segment. * @throws IndexOutOfBoundsException if {@code src.byteSize() > this.byteSize()}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code src} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code src.scope().isAccessibleBy(T) == false}. + * such that {@code src.isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}). * @return this segment. */ @@ -634,13 +818,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @return the relative offset, in bytes, of the first mismatch between this * and the given other segment, otherwise -1 if no mismatch * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code other} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code other.scope().isAccessibleBy(T) == false}. + * such that {@code other.isAccessibleBy(T) == false}. */ default long mismatch(MemorySegment other) { Objects.requireNonNull(other); @@ -666,9 +850,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * is resident in physical memory * * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. */ @@ -683,9 +867,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * occur.

    * * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. */ @@ -700,9 +884,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * occur (as this segment's contents might need to be paged back in).

    * * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. */ @@ -729,9 +913,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { *

    * * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. * @throws UncheckedIOException if there is an I/O error writing the contents of this segment to the associated storage device @@ -751,11 +935,11 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * returned if this segment' size is greater than {@link Integer#MAX_VALUE}. *

    * The life-cycle of the returned buffer will be tied to that of this segment. That is, accessing the returned buffer - * after the scope associated with this segment is no longer {@linkplain SegmentScope#isAlive() alive}, will + * after the scope associated with this segment is no longer {@linkplain Scope#isAlive() alive}, will * throw an {@link IllegalStateException}. Similarly, accessing the returned buffer from a thread {@code T} - * such that {@code scope().isAccessible(T) == false} will throw a {@link WrongThreadException}. + * such that {@code isAccessible(T) == false} will throw a {@link WrongThreadException}. *

    - * If this segment is associated with a scope that can only be accessed from a single thread, calling read/write I/O + * If this segment is accessible from a single thread, calling read/write I/O * operations on the resulting buffer might result in an unspecified exception being thrown. Examples of such problematic operations are * {@link java.nio.channels.AsynchronousSocketChannel#read(ByteBuffer)} and * {@link java.nio.channels.AsynchronousSocketChannel#write(ByteBuffer)}. @@ -776,9 +960,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new byte array whose contents are copied from this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code byte[]} instance, * e.g. its size is greater than {@link Integer#MAX_VALUE}. */ @@ -790,9 +974,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new short array whose contents are copied from this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code short[]} instance, * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE} */ @@ -804,9 +988,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new char array whose contents are copied from this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code char[]} instance, * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE}. */ @@ -818,9 +1002,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new int array whose contents are copied from this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code int[]} instance, * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. */ @@ -832,9 +1016,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new float array whose contents are copied from this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code float[]} instance, * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. */ @@ -846,9 +1030,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new long array whose contents are copied from this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code long[]} instance, * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. */ @@ -860,9 +1044,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new double array whose contents are copied from this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code double[]} instance, * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. */ @@ -882,9 +1066,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code S + offset > byteSize()}, where {@code S} is the size of the UTF-8 * string (including the terminator character). * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. */ default String getUtf8String(long offset) { return SharedUtils.toJavaStringInternal(this, offset); @@ -907,9 +1091,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param str the Java string to be written into this segment. * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code str.getBytes().length() + offset >= byteSize()}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. */ default void setUtf8String(long offset, String str) { Utils.toCString(str.getBytes(StandardCharsets.UTF_8), SegmentAllocator.prefixAllocator(asSlice(offset))); @@ -924,15 +1108,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * {@linkplain ByteBuffer#isReadOnly() read-only}. Moreover, if the buffer is a {@linkplain Buffer#isDirect() direct buffer}, * the returned segment is a native segment; otherwise the returned memory segment is a heap segment. *

    - * The scope {@code S} associated with the returned segment is computed as follows: - *

      - *
    • if the buffer has been obtained by calling {@link #asByteBuffer()} on a memory segment whose scope - * is {@code S'}, then {@code S = S'}; or
    • - *
    • if the buffer is a heap buffer, then {@code S} is the {@linkplain SegmentScope#global() global scope}; or - *
    • if the buffer is a direct buffer, then {@code S} is a scope that is always alive and which keeps the buffer reachable. - * Therefore, the off-heap region of memory backing the buffer instance will remain available as long as the - * returned segment is reachable.
    • - *
    + * If the provided buffer has been obtained by calling {@link #asByteBuffer()} on a memory segment whose + * {@linkplain Scope scope} is {@code S}, the returned segment will be associated with the + * same scope {@code S}. Otherwise, the scope of the returned segment is a fresh scope that is always alive. + *

    + * The scope associated with the returned segment keeps the provided buffer reachable. As such, if + * the provided buffer is a direct buffer, its backing memory region will not be deallocated as long as the + * returned segment (or any of its slices) are kept reachable. * * @param buffer the buffer instance to be turned into a new memory segment. * @return a memory segment, derived from the given buffer instance. @@ -947,8 +1129,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given byte array. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and - * its {@link #address()} is set to zero. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param byteArray the primitive array backing the heap memory segment. * @return a heap memory segment backed by a byte array. @@ -959,8 +1141,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given char array. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and - * its {@link #address()} is set to zero. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param charArray the primitive array backing the heap segment. * @return a heap memory segment backed by a char array. @@ -971,8 +1153,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given short array. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and - * its {@link #address()} is set to zero. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param shortArray the primitive array backing the heap segment. * @return a heap memory segment backed by a short array. @@ -983,8 +1165,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given int array. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and - * its {@link #address()} is set to zero. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param intArray the primitive array backing the heap segment. * @return a heap memory segment backed by an int array. @@ -995,8 +1177,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given float array. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and - * its {@link #address()} is set to zero. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param floatArray the primitive array backing the heap segment. * @return a heap memory segment backed by a float array. @@ -1007,8 +1189,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given long array. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and - * its {@link #address()} is set to zero. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param longArray the primitive array backing the heap segment. * @return a heap memory segment backed by a long array. @@ -1019,8 +1201,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given double array. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and - * its {@link #address()} is set to zero. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param doubleArray the primitive array backing the heap segment. * @return a heap memory segment backed by a double array. @@ -1032,16 +1214,16 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * A zero-length native segment modelling the {@code NULL} address. */ - MemorySegment NULL = NativeMemorySegmentImpl.makeNativeSegmentUnchecked(0L, 0); + MemorySegment NULL = new NativeMemorySegmentImpl(); /** * Creates a zero-length native segment from the given {@linkplain #address() address value}. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}. + * The returned segment is always accessible, from any thread. *

    - * This is equivalent to the following code: - * {@snippet lang = java: - * ofAddress(address, 0); - *} + * On 32-bit platforms, the given address value will be normalized such that the + * highest-order ("leftmost") 32 bits of the {@link MemorySegment#address() address} + * of the returned memory segment are set to zero. + * * @param address the address of the returned native segment. * @return a zero-length native segment with the given address. */ @@ -1049,208 +1231,6 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, 0); } - /** - * Creates a native segment with the given size and {@linkplain #address() address value}. - * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}. - *

    - * This is equivalent to the following code: - * {@snippet lang = java: - * ofAddress(address, byteSize, SegmentScope.global()); - *} - * This method is restricted. - * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. - * @param address the address of the returned native segment. - * @param byteSize the size (in bytes) of the returned native segment. - * @return a zero-length native segment with the given address and size. - * @throws IllegalArgumentException if {@code byteSize < 0}. - * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. - */ - @CallerSensitive - static MemorySegment ofAddress(long address, long byteSize) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemorySegment.class, "ofAddress"); - return MemorySegment.ofAddress(address, byteSize, SegmentScope.global()); - } - - /** - * Creates a native segment with the given size, address, and scope. - * This method can be useful when interacting with custom memory sources (e.g. custom allocators), - * where an address to some underlying region of memory is typically obtained from foreign code - * (often as a plain {@code long} value). - *

    - * The returned segment is not read-only (see {@link MemorySegment#isReadOnly()}), and is associated with the - * provided scope. - *

    - * This is equivalent to the following code: - * {@snippet lang = java: - * ofAddress(address, byteSize, scope, null); - *} - * This method is restricted. - * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. - * @param address the returned segment's address. - * @param byteSize the desired size. - * @param scope the scope associated with the returned native segment. - * @return a native segment with the given address, size and scope. - * @throws IllegalArgumentException if {@code byteSize < 0}. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. - * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. - */ - @CallerSensitive - @ForceInline - static MemorySegment ofAddress(long address, long byteSize, SegmentScope scope) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemorySegment.class, "ofAddress"); - Objects.requireNonNull(scope); - Utils.checkAllocationSizeAndAlign(byteSize, 1); - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, byteSize, scope, null); - } - - /** - * Creates a native segment with the given size, address, and scope. - * This method can be useful when interacting with custom memory sources (e.g. custom allocators), - * where an address to some underlying region of memory is typically obtained from foreign code - * (often as a plain {@code long} value). - *

    - * The returned segment is not read-only (see {@link MemorySegment#isReadOnly()}), and is associated with the - * provided scope. - *

    - * The provided cleanup action (if any) will be invoked when the scope becomes not {@linkplain SegmentScope#isAlive() alive}. - *

    - * Clients should ensure that the address and bounds refer to a valid region of memory that is accessible for reading and, - * if appropriate, writing; an attempt to access an invalid address from Java code will either return an arbitrary value, - * have no visible effect, or cause an unspecified exception to be thrown. - *

    - * This method is restricted. - * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. - * - * - * @param address the returned segment's address. - * @param byteSize the desired size. - * @param scope the scope associated with the returned native segment. - * @param cleanupAction the custom cleanup action to be associated to the returned segment (can be null). - * @return a native segment with the given address, size and scope. - * @throws IllegalArgumentException if {@code byteSize < 0}. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. - * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. - */ - @CallerSensitive - static MemorySegment ofAddress(long address, long byteSize, SegmentScope scope, Runnable cleanupAction) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemorySegment.class, "ofAddress"); - Objects.requireNonNull(scope); - Utils.checkAllocationSizeAndAlign(byteSize, 1); - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, byteSize, scope, cleanupAction); - } - - /** - * Creates a native segment with the given layout and scope. - *

    - * The lifetime off-heap region of memory associated with the returned native segment is determined by the - * provided scope. The off-heap memory region is deallocated when the scope becomes not - * {@linkplain SegmentScope#isAlive() alive}. If the scope has been obtained using an {@link Arena}, - * clients are responsible for ensuring that the arena is closed when the returned segment is no longer in use - * Failure to do so will result in off-heap memory leaks. As an alternative, an {@linkplain SegmentScope#auto() automatic scope} - * can be used, allowing the off-heap memory region associated with the returned native segment to be - * automatically released some unspecified time after the scope is no longer referenced. - *

    - * The {@linkplain #address() address} of the returned memory segment is the starting address of - * the newly allocated off-heap region backing the segment. Moreover, the {@linkplain #address() address} - * of the returned segment will be aligned according to the alignment constraint of the provided layout. - *

    - * This is equivalent to the following code: - * {@snippet lang=java : - * allocateNative(layout.bytesSize(), layout.bytesAlignment(), scope); - * } - *

    - * The region of off-heap region backing the returned native segment is initialized to zero. - * - * @param layout the layout of the off-heap memory region backing the native segment. - * @param scope the scope associated with the returned native segment. - * @return a new native segment. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. - */ - static MemorySegment allocateNative(MemoryLayout layout, SegmentScope scope) { - Objects.requireNonNull(layout); - Objects.requireNonNull(scope); - return allocateNative(layout.byteSize(), layout.byteAlignment(), scope); - } - - /** - * Creates a native segment with the given size (in bytes) and scope. - *

    - * The lifetime off-heap region of memory associated with the returned native segment is determined by the - * provided scope. The off-heap memory region is deallocated when the scope becomes not - * {@linkplain SegmentScope#isAlive() alive}. If the scope has been obtained using an {@link Arena}, - * clients are responsible for ensuring that the arena is closed when the returned segment is no longer in use - * Failure to do so will result in off-heap memory leaks. As an alternative, an {@linkplain SegmentScope#auto() automatic scope} - * can be used, allowing the off-heap memory region associated with the returned native segment to be - * automatically released some unspecified time after the scope is no longer referenced. - *

    - * The {@linkplain #address() address} of the returned memory segment is the starting address of - * the newly allocated off-heap region backing the segment. Moreover, the {@linkplain #address() address} - * of the returned segment is guaranteed to be at least 1-byte aligned. - *

    - * This is equivalent to the following code: - * {@snippet lang=java : - * allocateNative(bytesSize, 1, scope); - * } - *

    - * The region of off-heap region backing the returned native segment is initialized to zero. - * - * @param byteSize the size (in bytes) of the off-heap memory region of memory backing the native memory segment. - * @param scope the scope associated with the returned native segment. - * @return a new native memory segment. - * @throws IllegalArgumentException if {@code byteSize < 0}. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. - */ - static MemorySegment allocateNative(long byteSize, SegmentScope scope) { - return allocateNative(byteSize, 1, scope); - } - - /** - * Creates a native segment with the given size (in bytes), alignment (in bytes) and scope. - *

    - * The lifetime off-heap region of memory associated with the returned native segment is determined by the - * provided scope. The off-heap memory region is deallocated when the scope becomes not - * {@linkplain SegmentScope#isAlive() alive}. If the scope has been obtained using an {@link Arena}, - * clients are responsible for ensuring that the arena is closed when the returned segment is no longer in use - * Failure to do so will result in off-heap memory leaks. As an alternative, an {@linkplain SegmentScope#auto() automatic scope} - * can be used, allowing the off-heap memory region associated with the returned native segment to be - * automatically released some unspecified time after the scope is no longer referenced. - *

    - * The {@linkplain #address() address} of the returned memory segment is the starting address of - * the newly allocated off-heap region backing the segment. Moreover, the {@linkplain #address() address} - * of the returned segment will be aligned according to the provided alignment constraint. - *

    - * The region of off-heap region backing the returned native segment is initialized to zero. - * - * @param byteSize the size (in bytes) of the off-heap region of memory backing the native memory segment. - * @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment. - * @param scope the scope associated with the returned native segment. - * @return a new native memory segment. - * @throws IllegalArgumentException if {@code byteSize < 0}, {@code byteAlignment <= 0}, or if {@code byteAlignment} - * is not a power of 2. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. - */ - static MemorySegment allocateNative(long byteSize, long byteAlignment, SegmentScope scope) { - Objects.requireNonNull(scope); - Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); - return NativeMemorySegmentImpl.makeNativeSegment(byteSize, byteAlignment, scope); - } - /** * Performs a bulk copy from source segment to destination segment. More specifically, the bytes at offset * {@code srcOffset} through {@code srcOffset + bytes - 1} in the source segment are copied into the destination @@ -1275,20 +1255,21 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param dstOffset the starting offset, in bytes, of the destination segment. * @param bytes the number of bytes to be copied. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code srcSegment.scope().isAccessibleBy(T) == false}. + * such that {@code srcSegment.isAccessibleBy(T) == false}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code dstSegment.scope().isAccessibleBy(T) == false}. + * such that {@code dstSegment.isAccessibleBy(T) == false}. * @throws IndexOutOfBoundsException if {@code srcOffset + bytes > srcSegment.byteSize()} or if * {@code dstOffset + bytes > dstSegment.byteSize()}, or if either {@code srcOffset}, {@code dstOffset} * or {@code bytes} are {@code < 0}. * @throws UnsupportedOperationException if the destination segment is read-only (see {@link #isReadOnly()}). */ @ForceInline - static void copy(MemorySegment srcSegment, long srcOffset, MemorySegment dstSegment, long dstOffset, long bytes) { + static void copy(MemorySegment srcSegment, long srcOffset, + MemorySegment dstSegment, long dstOffset, long bytes) { copy(srcSegment, ValueLayout.JAVA_BYTE, srcOffset, dstSegment, ValueLayout.JAVA_BYTE, dstOffset, bytes); } @@ -1322,50 +1303,27 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * incompatible with the alignment constraint in the source * (resp. destination) element layout, or if the source (resp. destination) element layout alignment is greater than its size. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code srcSegment().scope().isAccessibleBy(T) == false}. + * such that {@code srcSegment().isAccessibleBy(T) == false}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code dstSegment().scope().isAccessibleBy(T) == false}. + * such that {@code dstSegment().isAccessibleBy(T) == false}. * @throws IndexOutOfBoundsException if {@code srcOffset + (elementCount * S) > srcSegment.byteSize()} or if * {@code dstOffset + (elementCount * S) > dstSegment.byteSize()}, where {@code S} is the byte size * of the element layouts, or if either {@code srcOffset}, {@code dstOffset} or {@code elementCount} are {@code < 0}. * @throws UnsupportedOperationException if the destination segment is read-only (see {@link #isReadOnly()}). */ @ForceInline - static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long srcOffset, MemorySegment dstSegment, - ValueLayout dstElementLayout, long dstOffset, long elementCount) { + static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long srcOffset, + MemorySegment dstSegment, ValueLayout dstElementLayout, long dstOffset, + long elementCount) { Objects.requireNonNull(srcSegment); Objects.requireNonNull(srcElementLayout); Objects.requireNonNull(dstSegment); Objects.requireNonNull(dstElementLayout); - AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment; - AbstractMemorySegmentImpl dstImpl = (AbstractMemorySegmentImpl)dstSegment; - if (srcElementLayout.byteSize() != dstElementLayout.byteSize()) { - throw new IllegalArgumentException("Source and destination layouts must have same size"); - } - Utils.checkElementAlignment(srcElementLayout, "Source layout alignment greater than its size"); - Utils.checkElementAlignment(dstElementLayout, "Destination layout alignment greater than its size"); - if (!srcImpl.isAlignedForElement(srcOffset, srcElementLayout)) { - throw new IllegalArgumentException("Source segment incompatible with alignment constraints"); - } - if (!dstImpl.isAlignedForElement(dstOffset, dstElementLayout)) { - throw new IllegalArgumentException("Destination segment incompatible with alignment constraints"); - } - long size = elementCount * srcElementLayout.byteSize(); - srcImpl.checkAccess(srcOffset, size, true); - dstImpl.checkAccess(dstOffset, size, false); - if (srcElementLayout.byteSize() == 1 || srcElementLayout.order() == dstElementLayout.order()) { - ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(srcImpl.sessionImpl(), dstImpl.sessionImpl(), - srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, - dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstOffset, size); - } else { - ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.sessionImpl(), dstImpl.sessionImpl(), - srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, - dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstOffset, size, srcElementLayout.byteSize()); - } + AbstractMemorySegmentImpl.copy(srcSegment, srcElementLayout, srcOffset, dstSegment, dstElementLayout, dstOffset, elementCount); } /** @@ -1375,9 +1333,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a byte value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1395,9 +1353,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the byte value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1416,9 +1374,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a boolean value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1436,9 +1394,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the boolean value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1457,9 +1415,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a char value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1477,9 +1435,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the char value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1498,9 +1456,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a short value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1518,9 +1476,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the short value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1539,9 +1497,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return an int value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1559,9 +1517,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the int value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1580,9 +1538,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a float value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1600,9 +1558,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the float value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1621,9 +1579,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a long value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1641,9 +1599,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the long value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1662,9 +1620,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a double value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1682,9 +1640,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the double value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1698,24 +1656,27 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in - * a native segment, associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, - * the size of the returned segment is {@code 0}. However, if the provided layout is an - * {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, then the size of the returned - * segment is {@code Long.MAX_VALUE}. + * a native segment, associated with a fresh scope that is always alive. Under normal conditions, + * the size of the returned segment is {@code 0}. However, if the provided address layout has a + * {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the returned segment + * is set to {@code T.byteSize()}. * @param layout the layout of the region of memory to be read. * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a native segment wrapping an address read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if provided address layout has a {@linkplain AddressLayout#targetLayout() target layout} + * {@code T}, and the address of the returned segment + * incompatible with the alignment constraint in {@code T}. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline - default MemorySegment get(ValueLayout.OfAddress layout, long offset) { + default MemorySegment get(AddressLayout layout, long offset) { return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, offset); } @@ -1726,9 +1687,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the address value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the @@ -1737,10 +1698,58 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws UnsupportedOperationException if {@code value} is not a {@linkplain #isNative() native} segment. */ @ForceInline - default void set(ValueLayout.OfAddress layout, long offset, MemorySegment value) { + default void set(AddressLayout layout, long offset, MemorySegment value) { ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, offset, value); } + /** + * Reads a byte from this segment at the given index, scaled by the given layout size. + * + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a byte value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain Scope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, + * or if the layout alignment is greater than its size. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default byte getAtIndex(ValueLayout.OfByte layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + // note: we know size is a small value (as it comes from ValueLayout::byteSize()) + return (byte) ((ValueLayouts.OfByteImpl) layout).accessHandle().get(this, index * layout.byteSize()); + } + + /** + * Reads a boolean from this segment at the given index, scaled by the given layout size. + * + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a boolean value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain Scope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, + * or if the layout alignment is greater than its size. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default boolean getAtIndex(ValueLayout.OfBoolean layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + // note: we know size is a small value (as it comes from ValueLayout::byteSize()) + return (boolean) ((ValueLayouts.OfBooleanImpl) layout).accessHandle().get(this, index * layout.byteSize()); + } + /** * Reads a char from this segment at the given index, scaled by the given layout size. * @@ -1749,9 +1758,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @return a char value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1773,9 +1782,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the char value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1798,9 +1807,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @return a short value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1814,6 +1823,57 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { return (short) ((ValueLayouts.OfShortImpl) layout).accessHandle().get(this, index * layout.byteSize()); } + /** + * Writes a byte into this segment at the given index, scaled by the given layout size. + * + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @param value the short value to be written. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain Scope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, + * or if the layout alignment is greater than its size. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the + * memory segment. + * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. + */ + @ForceInline + default void setAtIndex(ValueLayout.OfByte layout, long index, byte value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + // note: we know size is a small value (as it comes from ValueLayout::byteSize()) + ((ValueLayouts.OfByteImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); + + } + + /** + * Writes a boolean into this segment at the given index, scaled by the given layout size. + * + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @param value the short value to be written. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain Scope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, + * or if the layout alignment is greater than its size. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the + * memory segment. + * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. + */ + @ForceInline + default void setAtIndex(ValueLayout.OfBoolean layout, long index, boolean value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + // note: we know size is a small value (as it comes from ValueLayout::byteSize()) + ((ValueLayouts.OfBooleanImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); + } + /** * Writes a short into this segment at the given index, scaled by the given layout size. * @@ -1822,9 +1882,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the short value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1847,9 +1907,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @return an int value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1871,9 +1931,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the int value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1896,9 +1956,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @return a float value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1920,9 +1980,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the float value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1945,9 +2005,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @return a long value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1969,9 +2029,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the long value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -1994,9 +2054,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @return a double value read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -2018,9 +2078,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the double value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -2037,27 +2097,29 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in - * a native segment, associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, - * the size of the returned segment is {@code 0}. However, if the provided layout is an - * {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, then the size of the returned - * segment is {@code Long.MAX_VALUE}. - * + * a native segment, associated with a fresh scope that is always alive. Under normal conditions, + * the size of the returned segment is {@code 0}. However, if the provided address layout has a + * {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the returned segment + * is set to {@code T.byteSize()}. * @param layout the layout of the region of memory to be read. * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation * will occur can be expressed as {@code (index * layout.byteSize())}. * @return a native segment wrapping an address read from this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. + * @throws IllegalArgumentException if provided address layout has a {@linkplain AddressLayout#targetLayout() target layout} + * {@code T}, and the address of the returned segment + * incompatible with the alignment constraint in {@code T}. * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline - default MemorySegment getAtIndex(ValueLayout.OfAddress layout, long index) { + default MemorySegment getAtIndex(AddressLayout layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, index * layout.byteSize()); @@ -2071,9 +2133,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the address value to be written. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope().isAccessibleBy(T) == false}. + * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. @@ -2083,7 +2145,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws UnsupportedOperationException if {@code value} is not a {@linkplain #isNative() native} segment. */ @ForceInline - default void setAtIndex(ValueLayout.OfAddress layout, long index, MemorySegment value) { + default void setAtIndex(AddressLayout layout, long index, MemorySegment value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); @@ -2133,7 +2195,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param dstIndex the starting index of the destination array. * @param elementCount the number of array elements to be copied. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code srcSegment().isAccessibleBy(T) == false}. * @throws IllegalArgumentException if {@code dstArray} is not an array, or if it is an array but whose type is not supported, @@ -2167,7 +2229,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param dstOffset the starting offset, in bytes, of the destination segment. * @param elementCount the number of array elements to be copied. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code dstSegment().isAccessibleBy(T) == false}. * @throws IllegalArgumentException if {@code srcArray} is not an array, or if it is an array but whose type is not supported, @@ -2209,13 +2271,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @return the relative offset, in bytes, of the first mismatch between the source and destination segments, * otherwise -1 if no mismatch. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code srcSegment.scope().isAccessibleBy(T) == false}. + * such that {@code srcSegment.isAccessibleBy(T) == false}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not - * {@linkplain SegmentScope#isAlive() alive}. + * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code dstSegment.scope().isAccessibleBy(T) == false}. + * such that {@code dstSegment.isAccessibleBy(T) == false}. * @throws IndexOutOfBoundsException if {@code srcFromOffset < 0}, {@code srcToOffset < srcFromOffset} or * {@code srcToOffset > srcSegment.byteSize()} * @throws IndexOutOfBoundsException if {@code dstFromOffset < 0}, {@code dstToOffset < dstFromOffset} or @@ -2230,4 +2292,38 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { dstSegment, dstFromOffset, dstToOffset); } + /** + * A scope models the lifetime of all the memory segments associated with it. That is, a memory segment + * cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. A new scope is typically + * obtained indirectly, by creating a new {@linkplain Arena arena}. + *

    + * Scope instances can be compared for equality. That is, two scopes + * are considered {@linkplain #equals(Object)} if they denote the same lifetime. + */ + @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) + sealed interface Scope permits MemorySessionImpl { + /** + * {@return {@code true}, if the regions of memory backing the memory segments associated with this scope are + * still valid} + */ + boolean isAlive(); + + /** + * {@return {@code true}, if the provided object is also a scope, which models the same lifetime as that + * modelled by this scope}. In that case, it is always the case that + * {@code this.isAlive() == ((Scope)that).isAlive()}. + * @param that the object to be tested. + */ + @Override + boolean equals(Object that); + + /** + * Returns the hash code of this scope object. + * @implSpec Implementations of this method obey the general contract of {@link Object#hashCode}. + * @return the hash code of this scope object. + * @see #equals(Object) + */ + @Override + int hashCode(); + } } diff --git a/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java b/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java index 47848641edc..a09689c874c 100644 --- a/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java @@ -50,5 +50,12 @@ public sealed interface PaddingLayout extends MemoryLayout permits PaddingLayout * {@inheritDoc} */ @Override + PaddingLayout withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override PaddingLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java index e7a3c7efb5e..54c070001cc 100644 --- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java +++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -32,7 +32,6 @@ import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.function.Function; import jdk.internal.foreign.AbstractMemorySegmentImpl; -import jdk.internal.foreign.MemorySessionImpl; import jdk.internal.foreign.SlicingAllocator; import jdk.internal.foreign.Utils; import jdk.internal.javac.PreviewFeature; @@ -46,8 +45,6 @@ import jdk.internal.javac.PreviewFeature; *

    * This interface also defines factories for commonly used allocators: *

      - *
    • {@link #nativeAllocator(SegmentScope)} obtains a simple allocator which can - * be used to allocate native segments;
    • *
    • {@link #slicingAllocator(MemorySegment)} obtains an efficient slicing allocator, where memory * is allocated by repeatedly slicing the provided memory segment;
    • *
    • {@link #prefixAllocator(MemorySegment)} obtains an allocator which wraps a segment (either on-heap or off-heap) @@ -201,7 +198,7 @@ public interface SegmentAllocator { * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. */ - default MemorySegment allocate(ValueLayout.OfAddress layout, MemorySegment value) { + default MemorySegment allocate(AddressLayout layout, MemorySegment value) { Objects.requireNonNull(value); Objects.requireNonNull(layout); MemorySegment segment = allocate(layout); @@ -363,8 +360,8 @@ public interface SegmentAllocator { /** * Returns a segment allocator which responds to allocation requests by recycling a single segment. Each - * new allocation request will return a new slice starting at the segment offset {@code 0} (alignment - * constraints are ignored by this allocator), hence the name prefix allocator. + * new allocation request will return a new slice starting at the segment offset {@code 0}, hence the name + * prefix allocator. * Equivalent to (but likely more efficient than) the following code: * {@snippet lang=java : * MemorySegment segment = ... @@ -384,31 +381,4 @@ public interface SegmentAllocator { static SegmentAllocator prefixAllocator(MemorySegment segment) { return (AbstractMemorySegmentImpl)Objects.requireNonNull(segment); } - - /** - * Simple allocator used to allocate native segments. The returned allocator responds to an allocation request by - * returning a native segment backed by a fresh off-heap region of memory, with given byte size and alignment constraint. - *

      - * Each native segment obtained by the returned allocator is associated with the provided scope. As such, - * the off-heap region which backs the returned segment is freed when the scope becomes not - * {@linkplain SegmentScope#isAlive() alive}. - *

      - * The {@link MemorySegment#address()} of the native segments obtained by the returned allocator is the starting address of - * the newly allocated off-heap memory region backing the segment. Moreover, the {@linkplain MemorySegment#address() address} - * of the native segment will be aligned according the provided alignment constraint. - *

      - * The off-heap region of memory backing a native segment obtained by the returned allocator is initialized to zero. - *

      - * This is equivalent to the following code: - * {@snippet lang = java: - * SegmentAllocator nativeAllocator = (byteSize, byteAlignment) -> - * MemorySegment.allocateNative(byteSize, byteAlignment, scope); - * } - * @param scope the scope associated with the segments returned by the native allocator. - * @return a simple allocator used to allocate native segments. - */ - static SegmentAllocator nativeAllocator(SegmentScope scope) { - Objects.requireNonNull(scope); - return (MemorySessionImpl)scope; - } } diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentScope.java b/src/java.base/share/classes/java/lang/foreign/SegmentScope.java deleted file mode 100644 index a85dec0fb28..00000000000 --- a/src/java.base/share/classes/java/lang/foreign/SegmentScope.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2022, 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 java.lang.foreign; - -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.javac.PreviewFeature; -import jdk.internal.ref.CleanerFactory; - -/** - * A segment scope controls access to memory segments. - *

      - * A memory segment can only be accessed while its scope is {@linkplain #isAlive() alive}. Moreover, - * depending on how the segment scope has been obtained, access might additionally be - * restricted to specific threads. - *

      - * The simplest segment scope is the {@linkplain SegmentScope#global() global scope}. The global scope - * is always alive. As a result, segments associated with the global scope are always accessible and their backing - * regions of memory are never deallocated. Moreover, memory segments associated with the global scope - * can be {@linkplain #isAccessibleBy(Thread) accessed} from any thread. - * {@snippet lang = java: - * MemorySegment segment = MemorySegment.allocateNative(100, SegmentScope.global()); - * ... - * // segment is never deallocated! - *} - *

      - * Alternatively, clients can obtain an {@linkplain SegmentScope#auto() automatic scope}, that is a segment - * scope that is managed, automatically, by the garbage collector. The regions of memory backing memory segments associated - * with an automatic scope are deallocated at some unspecified time after they become - * unreachable, as shown below: - * - * {@snippet lang = java: - * MemorySegment segment = MemorySegment.allocateNative(100, SegmentScope.auto()); - * ... - * segment = null; // the segment region becomes available for deallocation after this point - *} - * Memory segments associated with an automatic scope can also be {@linkplain #isAccessibleBy(Thread) accessed} from any thread. - *

      - * Finally, clients can obtain a segment scope from an existing {@linkplain Arena arena}, the arena scope. The regions of memory - * backing memory segments associated with an arena scope are deallocated when the arena is {@linkplain Arena#close() closed}. - * When this happens, the arena scope becomes not {@linkplain #isAlive() alive} and subsequent access operations on segments - * associated with the arena scope will fail {@link IllegalStateException}. - * - * {@snippet lang = java: - * MemorySegment segment = null; - * try (Arena arena = Arena.openConfined()) { - * segment = MemorySegment.allocateNative(100, arena.scope()); - * ... - * } // segment region deallocated here - * segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException - * } - * - * Which threads can {@link #isAccessibleBy(Thread) access} memory segments associated with an arena scope depends - * on the arena kind. For instance, segments associated with the scope of a {@linkplain Arena#openConfined() confined arena} - * can only be accessed by the thread that created the arena. Conversely, segments associated with the scope of - * {@linkplain Arena#openConfined() shared arena} can be accessed by any thread. - * - * @implSpec - * Implementations of this interface are thread-safe. - * - * @see Arena - * @see MemorySegment - * - * @since 20 - */ -@PreviewFeature(feature =PreviewFeature.Feature.FOREIGN) -sealed public interface SegmentScope permits MemorySessionImpl { - - /** - * Creates a new scope that is managed, automatically, by the garbage collector. - * Segments associated with the returned scope can be - * {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. - * - * @return a new scope that is managed, automatically, by the garbage collector. - */ - static SegmentScope auto() { - return MemorySessionImpl.createImplicit(CleanerFactory.cleaner()); - } - - /** - * Obtains the global scope. Segments associated with the global scope can be - * {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. - * - * @return the global scope. - */ - static SegmentScope global() { - return MemorySessionImpl.GLOBAL; - } - - /** - * {@return {@code true}, if this scope is alive} - */ - boolean isAlive(); - - /** - * {@return {@code true} if the provided thread can access and/or associate segments with this scope} - * @param thread the thread to be tested. - */ - boolean isAccessibleBy(Thread thread); - - /** - * Runs a critical action while this scope is kept alive. - * @param action the action to be run. - * @throws IllegalStateException if this scope is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code isAccessibleBy(T) == false}. - */ - void whileAlive(Runnable action); - -} diff --git a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java index e73cac56851..3d0a01631f8 100644 --- a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -128,9 +128,22 @@ public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayo */ SequenceLayout flatten(); + /** + * {@inheritDoc} + */ @Override SequenceLayout withName(String name); + /** + * {@inheritDoc} + */ @Override + MemoryLayout withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + * @throws IllegalArgumentException if {@code bitAlignment < elementLayout().bitAlignment()}. + */ SequenceLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/StructLayout.java b/src/java.base/share/classes/java/lang/foreign/StructLayout.java index b94b6a97d85..ad314ae3b36 100644 --- a/src/java.base/share/classes/java/lang/foreign/StructLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/StructLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -39,9 +39,22 @@ import jdk.internal.javac.PreviewFeature; @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface StructLayout extends GroupLayout permits StructLayoutImpl { + /** + * {@inheritDoc} + */ @Override StructLayout withName(String name); + /** + * {@inheritDoc} + */ + @Override + StructLayout withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ @Override StructLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java index b2c5711922a..573d884bdc1 100644 --- a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java +++ b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -52,21 +52,23 @@ import java.util.function.BiFunction; *

        *
      • It can be passed to a {@link Linker} to create a downcall method handle, which can then be used to call the foreign function at the segment's address.
      • *
      • It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}, as an argument to the underlying foreign function.
      • - *
      • It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, MemorySegment) stored} inside another memory segment.
      • - *
      • It can be used to access the region of memory backing a global variable (this might require - * {@link MemorySegment#ofAddress(long, long, SegmentScope) resizing} the segment first).
      • + *
      • It can be {@linkplain MemorySegment#set(AddressLayout, long, MemorySegment) stored} inside another memory segment.
      • + *
      • It can be used to access the region of memory backing a global variable (this requires + * {@link MemorySegment#reinterpret(long)} () resizing} the segment first).
      • *
      * *

      Obtaining a symbol lookup

      * - * The factory methods {@link #libraryLookup(String, SegmentScope)} and {@link #libraryLookup(Path, SegmentScope)} + * The factory methods {@link #libraryLookup(String, Arena)} and {@link #libraryLookup(Path, Arena)} * create a symbol lookup for a library known to the operating system. The library is specified by either its name or a path. - * The library is loaded if not already loaded. The symbol lookup, which is known as a library lookup, is associated - * with a {@linkplain SegmentScope scope}; when the scope becomes not {@link SegmentScope#isAlive()}, the library is unloaded: + * The library is loaded if not already loaded. The symbol lookup, which is known as a library lookup, and its + * lifetime is controlled by an {@linkplain Arena arena}. For instance, if the provided arena is a + * confined arena, the library associated with the symbol lookup is unloaded when the confined arena + * is {@linkplain Arena#close()}: * * {@snippet lang = java: - * try (Arena arena = Arena.openConfined()) { - * SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena.scope()); // libGL.so loaded here + * try (Arena arena = Arena.ofConfined()) { + * SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena); // libGL.so loaded here * MemorySegment glGetString = libGL.find("glGetString").orElseThrow(); * ... * } // libGL.so unloaded here @@ -92,7 +94,7 @@ import java.util.function.BiFunction; * that were loaded in the course of creating a library lookup: * * {@snippet lang = java: - * libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true + * libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true * loaderLookup().find("glGetString").isPresent(); // false *} * @@ -101,7 +103,7 @@ import java.util.function.BiFunction; * * {@snippet lang = java: * System.loadLibrary("GL"); // libGL.so loaded here - * libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true + * libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true *} * *

      @@ -139,9 +141,10 @@ public interface SymbolLookup { *

      * Libraries associated with a class loader are unloaded when the class loader becomes * unreachable. The symbol lookup - * returned by this method is backed by a scope that is always alive and which keeps the caller's - * class loader reachable. Therefore, libraries associated with the caller's class - * loader are kept loaded (and their symbols available) as long as a loader lookup for that class loader is reachable. + * returned by this method is associated with a fresh {@linkplain MemorySegment.Scope scope} which keeps the caller's + * class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded + * (and their symbols available) as long as a loader lookup for that class loader, or any of the segments + * obtained by it, is reachable. *

      * In cases where this method is called from a context where there is no caller frame on the stack * (e.g. when called directly from a JNI attached thread), the caller's class loader defaults to the @@ -158,9 +161,13 @@ public interface SymbolLookup { ClassLoader loader = caller != null ? caller.getClassLoader() : ClassLoader.getSystemClassLoader(); - SegmentScope loaderScope = (loader == null || loader instanceof BuiltinClassLoader) ? - SegmentScope.global() : // builtin loaders never go away - MemorySessionImpl.heapSession(loader); + Arena loaderArena;// builtin loaders never go away + if ((loader == null || loader instanceof BuiltinClassLoader)) { + loaderArena = Arena.global(); + } else { + MemorySessionImpl session = MemorySessionImpl.heapSession(loader); + loaderArena = session.asArena(); + } return name -> { Objects.requireNonNull(name); JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess(); @@ -168,14 +175,17 @@ public interface SymbolLookup { long addr = javaLangAccess.findNative(loader, name); return addr == 0L ? Optional.empty() : - Optional.of(MemorySegment.ofAddress(addr, 0L, loaderScope)); + Optional.of(MemorySegment.ofAddress(addr) + .reinterpret(loaderArena, null)); }; } /** * Loads a library with the given name (if not already loaded) and creates a symbol lookup for symbols in that library. - * The library will be unloaded when the provided scope becomes - * not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it. + * The lifetime of the returned library lookup is controlled by the provided arena. + * For instance, if the provided arena is a confined arena, the library + * associated with the returned lookup will be unloaded when the provided confined arena is + * {@linkplain Arena#close() closed}. * @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS, * the library name is resolved according to the specification of the {@code dlopen} function for that OS. * In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function. @@ -186,21 +196,26 @@ public interface SymbolLookup { * restricted methods, and use safe and supported functionalities, where possible. * * @param name the name of the library in which symbols should be looked up. - * @param scope the scope associated with symbols obtained from the returned lookup. + * @param arena the arena associated with symbols obtained from the returned lookup. * @return a new symbol lookup suitable to find symbols in a library with the given name. + * @throws IllegalStateException if {@code arena.scope().isAlive() == false} + * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a + * thread {@code T}, other than the arena's owner thread. * @throws IllegalArgumentException if {@code name} does not identify a valid library. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. */ @CallerSensitive - static SymbolLookup libraryLookup(String name, SegmentScope scope) { + static SymbolLookup libraryLookup(String name, Arena arena) { Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup"); - return libraryLookup(name, RawNativeLibraries::load, scope); + return libraryLookup(name, RawNativeLibraries::load, arena); } /** * Loads a library from the given path (if not already loaded) and creates a symbol lookup for symbols - * in that library. The library will be unloaded when the provided scope becomes - * not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it. + * in that library. The lifetime of the returned library lookup is controlled by the provided arena. + * For instance, if the provided arena is a confined arena, the library + * associated with the returned lookup will be unloaded when the provided confined arena is + * {@linkplain Arena#close() closed}. *

      * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash @@ -210,20 +225,23 @@ public interface SymbolLookup { * @implNote On Linux, the functionalities provided by this factory method and the returned symbol lookup are * implemented using the {@code dlopen}, {@code dlsym} and {@code dlclose} functions. * @param path the path of the library in which symbols should be looked up. - * @param scope the scope associated with symbols obtained from the returned lookup. + * @param arena the arena associated with symbols obtained from the returned lookup. * @return a new symbol lookup suitable to find symbols in a library with the given path. + * @throws IllegalStateException if {@code arena.scope().isAlive() == false} + * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a + * thread {@code T}, other than the arena's owner thread. * @throws IllegalArgumentException if {@code path} does not point to a valid library. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. */ @CallerSensitive - static SymbolLookup libraryLookup(Path path, SegmentScope scope) { + static SymbolLookup libraryLookup(Path path, Arena arena) { Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup"); - return libraryLookup(path, RawNativeLibraries::load, scope); + return libraryLookup(path, RawNativeLibraries::load, arena); } - private static SymbolLookup libraryLookup(Z libDesc, BiFunction loadLibraryFunc, SegmentScope libScope) { + private static SymbolLookup libraryLookup(Z libDesc, BiFunction loadLibraryFunc, Arena libArena) { Objects.requireNonNull(libDesc); - Objects.requireNonNull(libScope); + Objects.requireNonNull(libArena); // attempt to load native library from path or name RawNativeLibraries nativeLibraries = RawNativeLibraries.newInstance(MethodHandles.lookup()); NativeLibrary library = loadLibraryFunc.apply(nativeLibraries, libDesc); @@ -231,7 +249,7 @@ public interface SymbolLookup { throw new IllegalArgumentException("Cannot open library: " + libDesc); } // register hook to unload library when 'libScope' becomes not alive - ((MemorySessionImpl) libScope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { + MemorySessionImpl.toMemorySession(libArena).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { @Override public void cleanup() { nativeLibraries.unload(library); @@ -242,7 +260,8 @@ public interface SymbolLookup { long addr = library.find(name); return addr == 0L ? Optional.empty() : - Optional.of(MemorySegment.ofAddress(addr, 0, libScope)); + Optional.of(MemorySegment.ofAddress(addr) + .reinterpret(libArena, null)); }; } } diff --git a/src/java.base/share/classes/java/lang/foreign/UnionLayout.java b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java index 155f47bb4df..fa322f3cc74 100644 --- a/src/java.base/share/classes/java/lang/foreign/UnionLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -49,5 +49,12 @@ public sealed interface UnionLayout extends GroupLayout permits UnionLayoutImpl * {@inheritDoc} */ @Override + UnionLayout withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override UnionLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/VaList.java b/src/java.base/share/classes/java/lang/foreign/VaList.java deleted file mode 100644 index 5ce79bd2ae2..00000000000 --- a/src/java.base/share/classes/java/lang/foreign/VaList.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2021, 2022, 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 java.lang.foreign; - -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.function.Consumer; - -import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList; -import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList; -import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64VaList; -import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64VaList; -import jdk.internal.foreign.abi.x64.sysv.SysVVaList; -import jdk.internal.foreign.abi.x64.windows.WinVaList; -import jdk.internal.javac.PreviewFeature; -import jdk.internal.reflect.CallerSensitive; -import jdk.internal.reflect.Reflection; - -/** - * Helper class to create and manipulate variable argument lists, similar in functionality to a C {@code va_list}. - *

      - * A variable argument list can be created using the {@link #make(Consumer, SegmentScope)} factory, as follows: - * {@snippet lang = java: - * VaList vaList = VaList.make(builder -> - * builder.addVarg(C_INT, 42) - * .addVarg(C_DOUBLE, 3.8d)); - *} - * Once created, clients can obtain the platform-dependent {@linkplain #segment() memory segment} associated with a variable - * argument list, which can then be passed to {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handles} - * targeting native functions using the C {@code va_list} type. - *

      - * The contents of a foreign memory segment modelling a variable argument list can be accessed by unsafely creating - * a variable argument list, as follows: - * {@snippet lang = java: - * void upcall(int n, MemorySegment vaListSegment) { - * try (Arena arena = Arena.openConfined()) { - * VaList vaList = VaList.ofAddress(vaListSegment.address(), arena.scope()); - * VaList copy = vaList.copy(); - * int i = vaList.nextVarg(C_INT); - * double d = vaList.nextVarg(C_DOUBLE); - * // and again - * int i = copy.nextVarg(C_INT); - * double d = copy.nextVarg(C_DOUBLE); - * } - * } - *} - * The above method receives a foreign segment modelling a variable argument list; the contents of the segment are accessed by creating - * a new variable argument list, from the segment address. Note that the variable argument list is first copied into - * a second list before any element is accessed: this will allow us to iterate through the elements twice. Elements in - * the variable argument list are accessed using {@link #nextVarg(ValueLayout.OfInt)} and - * {@link #nextVarg(ValueLayout.OfDouble)}. These methods (as well as other access methods in the {@link VaList} class) - * take the layout of the element that needs to be accessed and perform all the necessary alignment checks as well - * as endianness conversions. - *

      - * Per the C specification (see C99 standard 6.5.2.2 Function calls - item 6), - * arguments to variadic calls are erased by way of 'default argument promotions', - * which erases integral types by way of integer promotion (see C99 standard 6.3.1.1 - item 2), - * and which erases all {@code float} arguments to {@code double}. - *

      - * As such, this interface only supports reading {@code int}, {@code double}, - * and any other type that fits into a {@code long}. - *

      Safety considerations

      - * Accessing a value through a variable argument list using the wrong memory layout will result in undefined behavior. - * For instance, if a variable argument list currently points at a C {@code int} value, then accessing it using - * {@link #nextVarg(ValueLayout.OfLong)} is illegal. Similarly, accessing the variable argument list with - * {@link #skip(MemoryLayout...)}, and providing a layout other than {@link ValueLayout.OfInt} is illegal. - * Any such illegal accesses might not be detected by the implementation, and can corrupt the variable argument list, - * so that the behavior of subsequent accesses is also undefined. - *

      - * It is possible for clients to access elements outside the spatial bounds of a variable argument list. - * Variable argument list implementations will try to detect out-of-bounds reads on a best-effort basis. - *

      - * Whether this detection succeeds depends on the factory method used to create the variable argument list: - *

        - *
      • Variable argument lists created safely, using {@link #make(Consumer, SegmentScope)} are capable of detecting out-of-bounds reads;
      • - *
      • Variable argument lists created unsafely, using {@link #ofAddress(long, SegmentScope)} are not capable of detecting out-of-bounds reads
      • - *
      - *

      - * This class is not thread safe, and all accesses should occur within a single thread - * (regardless of the scope used to obtain the variable arity list). - * - * @since 19 - */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, WindowsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList { - - /** - * Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this - * method is equivalent to the C {@code va_arg} function. - * - * @param layout the layout of the value to be read. - * @return the {@code int} value read from this variable argument list. - * @throws IllegalStateException if the scope associated with this variable argument list is not - * {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code segment().scope().isAccessibleBy(T) == false}. - * @throws NoSuchElementException if an out-of-bounds read is detected. - */ - int nextVarg(ValueLayout.OfInt layout); - - /** - * Reads the next value as a {@code long} and advances this variable argument list's position. The behavior of this - * method is equivalent to the C {@code va_arg} function. - * - * @param layout the layout of the value to be read. - * @return the {@code long} value read from this variable argument list. - * @throws IllegalStateException if the scope associated with this variable argument list is not - * {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code segment().scope().isAccessibleBy(T) == false}. - * @throws NoSuchElementException if an out-of-bounds read is detected. - */ - long nextVarg(ValueLayout.OfLong layout); - - /** - * Reads the next value as a {@code double} and advances this variable argument list's position. The behavior of this - * method is equivalent to the C {@code va_arg} function. - * - * @param layout the layout of the value - * @return the {@code double} value read from this variable argument list. - * @throws IllegalStateException if the scope associated with this variable argument list is not - * {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code segment().scope().isAccessibleBy(T) == false}. - * @throws NoSuchElementException if an out-of-bounds read is detected. - */ - double nextVarg(ValueLayout.OfDouble layout); - - /** - * Reads the next address value, wraps it into a native segment, and advances this variable argument list's position. - * The behavior of this method is equivalent to the C {@code va_arg} function. The returned segment's base - * {@linkplain MemorySegment#address()} is set to the value read from the variable argument list, and the segment - * is associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, the size of the returned - * segment is {@code 0}. However, if the provided layout is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} - * address layout, then the size of the returned segment is {@code Long.MAX_VALUE}. - * - * @param layout the layout of the value to be read. - * @return a native segment whose {@linkplain MemorySegment#address() address} is the value read from - * this variable argument list. - * @throws IllegalStateException if the scope associated with this variable argument list is not - * {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code segment().scope().isAccessibleBy(T) == false}. - * @throws NoSuchElementException if an out-of-bounds read is detected. - */ - MemorySegment nextVarg(ValueLayout.OfAddress layout); - - /** - * Reads the next composite value into a new {@code MemorySegment}, allocated with the provided allocator, - * and advances this variable argument list's position. The behavior of this method is equivalent to the C - * {@code va_arg} function. The provided group layout must correspond to a C struct or union type. - *

      - * How the value is read in the returned segment is ABI-dependent: calling this method on a group layout - * with member layouts {@code L_1, L_2, ... L_n} is not guaranteed to be semantically equivalent to perform distinct - * calls to {@code nextVarg} for each of the layouts in {@code L_1, L_2, ... L_n}. - *

      - * The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}. - * - * @param layout the layout of the value to be read. - * @param allocator the allocator to be used to create a segment where the contents of the variable argument list - * will be copied. - * @return the {@code MemorySegment} value read from this variable argument list. - * @throws IllegalStateException if the scope associated with this variable argument list is not - * {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code segment().scope().isAccessibleBy(T) == false}. - * @throws NoSuchElementException if an out-of-bounds read is detected. - */ - MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator); - - /** - * Skips a number of elements with the given memory layouts, and advances this variable argument list's position. - * - * @param layouts the layouts of the values to be skipped. - * @throws IllegalStateException if the scope associated with this variable argument list is not - * {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code segment().scope().isAccessibleBy(T) == false}. - * @throws NoSuchElementException if an out-of-bounds read is detected. - */ - void skip(MemoryLayout... layouts); - - /** - * Copies this variable argument list at its current position into a new variable argument list associated - * with the same scope as this variable argument list. The behavior of this method is equivalent to the C - * {@code va_copy} function. - *

      - * Copying is useful to traverse the variable argument list elements, starting from the current position, - * without affecting the state of the original variable argument list, essentially allowing the elements to be - * traversed multiple times. - * - * @return a copy of this variable argument list. - * @throws IllegalStateException if the scope associated with this variable argument list is not - * {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code segment().scope().isAccessibleBy(T) == false}. - */ - VaList copy(); - - /** - * Returns a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list. - * The contents of the returned memory segment are platform-dependent. Whether and how the contents of - * the returned segment are updated when iterating the contents of a variable argument list is also - * platform-dependent. - * @return a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list. - */ - MemorySegment segment(); - - /** - * Creates a variable argument list from the give address value and scope. The address is typically obtained - * by calling {@link MemorySegment#address()} on a foreign memory segment instance. The provided scope determines - * the lifecycle of the returned variable argument list: the returned variable argument list will no longer be accessible, - * and its associated off-heap memory region will be deallocated when the scope becomes not - * {@linkplain SegmentScope#isAlive() alive}. - *

      - * This method is restricted. - * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. - * - * @param address the address of the variable argument list. - * @param scope the scope associated with the returned variable argument list. - * @return a new variable argument list backed by an off-heap region of memory starting at the given address value. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. - * @throws UnsupportedOperationException if the underlying native platform is not supported. - * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. - */ - @CallerSensitive - static VaList ofAddress(long address, SegmentScope scope) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), VaList.class, "ofAddress"); - Objects.requireNonNull(scope); - return SharedUtils.newVaListOfAddress(address, scope); - } - - /** - * Creates a variable argument list using a builder (see {@link Builder}), with the given - * scope. The provided scope determines the lifecycle of the returned variable argument list: the - * returned variable argument list will no longer be accessible, and its associated off-heap memory region will be - * deallocated when the scope becomes not {@linkplain SegmentScope#isAlive() alive}. - *

      - * Note that when there are no elements added to the created va list, - * this method will return the same as {@link #empty()}. - * - * @implNote variable argument lists created using this method can detect out-of-bounds reads. - * - * @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements - * of the underlying variable argument list. - * @param scope the scope to be associated with the new variable arity list. - * @return a new variable argument list. - * @throws UnsupportedOperationException if the underlying native platform is not supported. - * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code scope.isAccessibleBy(T) == false}. - */ - static VaList make(Consumer actions, SegmentScope scope) { - Objects.requireNonNull(actions); - Objects.requireNonNull(scope); - return SharedUtils.newVaList(actions, scope); - } - - /** - * Returns an empty variable argument list, associated with the {@linkplain SegmentScope#global() global scope}. - * The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException} - * on all operations, except for {@link VaList#segment()}, {@link VaList#copy()}. - * @return an empty variable argument list. - * @throws UnsupportedOperationException if the underlying native platform is not supported. - */ - static VaList empty() { - return SharedUtils.emptyVaList(); - } - - /** - * A builder used to construct a {@linkplain VaList variable argument list}. - * - * @since 19 - */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) - sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, WindowsAArch64VaList.Builder, LinuxRISCV64VaList.Builder { - - /** - * Writes an {@code int} value to the variable argument list being constructed. - * - * @param layout the layout of the value to be written. - * @param value the {@code int} value to be written. - * @return this builder. - */ - Builder addVarg(ValueLayout.OfInt layout, int value); - - /** - * Writes a {@code long} value to the variable argument list being constructed. - * - * @param layout the layout of the value to be written. - * @param value the {@code long} value to be written. - * @return this builder. - */ - Builder addVarg(ValueLayout.OfLong layout, long value); - - /** - * Writes a {@code double} value to the variable argument list being constructed. - * - * @param layout the layout of the value to be written. - * @param value the {@code double} value to be written. - * @return this builder. - */ - Builder addVarg(ValueLayout.OfDouble layout, double value); - - /** - * Writes the {@linkplain MemorySegment#address() address} of the provided native segment - * to the variable argument list being constructed. - * - * @param layout the layout of the value to be written. - * @param segment the segment whose {@linkplain MemorySegment#address() address} is to be written. - * @return this builder. - */ - Builder addVarg(ValueLayout.OfAddress layout, MemorySegment segment); - - /** - * Writes a {@code MemorySegment}, with the given layout, to the variable argument list being constructed. - * - * @param layout the layout of the value to be written. - * @param value the {@code MemorySegment} whose contents will be copied. - * @return this builder. - */ - Builder addVarg(GroupLayout layout, MemorySegment value); - } -} diff --git a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java index 9ff1f08d7b9..673c79c1a18 100644 --- a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -31,7 +31,6 @@ import java.nio.ByteOrder; import jdk.internal.foreign.layout.ValueLayouts; import jdk.internal.javac.PreviewFeature; -import jdk.internal.reflect.CallerSensitive; /** * A layout that models values of basic data types. Examples of values modelled by a value layout are @@ -49,10 +48,13 @@ import jdk.internal.reflect.CallerSensitive; * * @implSpec implementing classes and subclasses are immutable, thread-safe and value-based. * + * @sealedGraph * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed interface ValueLayout extends MemoryLayout { +public sealed interface ValueLayout extends MemoryLayout permits + ValueLayout.OfBoolean, ValueLayout.OfByte, ValueLayout.OfChar, ValueLayout.OfShort, ValueLayout.OfInt, + ValueLayout.OfFloat, ValueLayout.OfLong, ValueLayout.OfDouble, AddressLayout { /** * {@return the value's byte order} @@ -68,6 +70,12 @@ public sealed interface ValueLayout extends MemoryLayout { */ ValueLayout withOrder(ByteOrder order); + /** + * {@inheritDoc} + */ + @Override + ValueLayout withoutName(); + /** * Creates a strided var handle that can be used to access a memory segment as multi-dimensional * array. The layout of this array is a sequence layout with {@code shape.length} nested sequence layouts. The element @@ -141,9 +149,9 @@ public sealed interface ValueLayout extends MemoryLayout { /** * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} */ @Override - ValueLayout withBitAlignment(long bitAlignment); /** @@ -165,6 +173,13 @@ public sealed interface ValueLayout extends MemoryLayout { * {@inheritDoc} */ @Override + OfBoolean withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfBoolean withBitAlignment(long bitAlignment); /** @@ -194,6 +209,13 @@ public sealed interface ValueLayout extends MemoryLayout { * {@inheritDoc} */ @Override + OfByte withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfByte withBitAlignment(long bitAlignment); /** @@ -224,6 +246,13 @@ public sealed interface ValueLayout extends MemoryLayout { * {@inheritDoc} */ @Override + OfChar withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfChar withBitAlignment(long bitAlignment); /** @@ -254,6 +283,13 @@ public sealed interface ValueLayout extends MemoryLayout { * {@inheritDoc} */ @Override + OfShort withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfShort withBitAlignment(long bitAlignment); /** @@ -284,6 +320,13 @@ public sealed interface ValueLayout extends MemoryLayout { * {@inheritDoc} */ @Override + OfInt withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfInt withBitAlignment(long bitAlignment); /** @@ -310,6 +353,12 @@ public sealed interface ValueLayout extends MemoryLayout { @Override OfFloat withName(String name); + /** + * {@inheritDoc} + */ + @Override + OfFloat withoutName(); + /** * {@inheritDoc} */ @@ -344,6 +393,13 @@ public sealed interface ValueLayout extends MemoryLayout { * {@inheritDoc} */ @Override + OfLong withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfLong withBitAlignment(long bitAlignment); /** @@ -374,6 +430,13 @@ public sealed interface ValueLayout extends MemoryLayout { * {@inheritDoc} */ @Override + OfDouble withoutName(); + + /** + * {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfDouble withBitAlignment(long bitAlignment); /** @@ -384,146 +447,57 @@ public sealed interface ValueLayout extends MemoryLayout { } - /** - * A value layout whose carrier is {@code MemorySegment.class}. - * - * @see #ADDRESS - * @see #ADDRESS_UNALIGNED - * @since 19 - */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfAddress extends ValueLayout permits ValueLayouts.OfAddressImpl { - - /** - * {@inheritDoc} - */ - @Override - OfAddress withName(String name); - - /** - * {@inheritDoc} - */ - @Override - OfAddress withBitAlignment(long bitAlignment); - - /** - * {@inheritDoc} - */ - @Override - OfAddress withOrder(ByteOrder order); - - /** - * Returns an unbounded address layout with the same carrier, alignment constraint, name and order as this address layout, - * but with the specified pointee layout. An unbounded address layout allow raw addresses to be accessed - * as {@linkplain MemorySegment memory segments} whose size is set to {@link Long#MAX_VALUE}. As such, - * these segments can be used in subsequent access operations. - *

      - * This method is restricted. - * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. - * - * @return an unbounded address layout with same characteristics as this layout. - * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. - * @see #isUnbounded() - */ - @CallerSensitive - OfAddress asUnbounded(); - - /** - * {@return {@code true}, if this address layout is an {@linkplain #asUnbounded() unbounded address layout}}. - */ - boolean isUnbounded(); - - } - /** * A value layout constant whose size is the same as that of a machine address ({@code size_t}), * bit alignment set to {@code sizeof(size_t) * 8}, byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(MemorySegment.class, ByteOrder.nativeOrder()); - * } */ - OfAddress ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder()); + AddressLayout ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code byte}, * bit alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder()); - * } */ OfByte JAVA_BYTE = ValueLayouts.OfByteImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code boolean}, * bit alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder()); - * } */ OfBoolean JAVA_BOOLEAN = ValueLayouts.OfBooleanImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code char}, * bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()); - * } */ OfChar JAVA_CHAR = ValueLayouts.OfCharImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code short}, * bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder()); - * } */ OfShort JAVA_SHORT = ValueLayouts.OfShortImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code int}, * bit alignment set to 32, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder()); - * } */ OfInt JAVA_INT = ValueLayouts.OfIntImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code long}, * bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder()); - * } */ OfLong JAVA_LONG = ValueLayouts.OfLongImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code float}, * bit alignment set to 32, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(float.class, ByteOrder.nativeOrder()).withBitAlignment(32); - * } */ OfFloat JAVA_FLOAT = ValueLayouts.OfFloatImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code double}, * bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}. - * Equivalent to the following code: - * {@snippet lang=java : - * MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder()); - * } */ OfDouble JAVA_DOUBLE = ValueLayouts.OfDoubleImpl.of(ByteOrder.nativeOrder()); @@ -537,7 +511,7 @@ public sealed interface ValueLayout extends MemoryLayout { * @apiNote Care should be taken when using unaligned value layouts as they may induce * performance and portability issues. */ - OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8); + AddressLayout ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8); /** * An unaligned value layout constant whose size is the same as that of a Java {@code char} diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index 80dac0f01a3..a0ec03854c9 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -31,77 +31,48 @@ * *

      * The main abstraction introduced to support foreign memory access is {@link java.lang.foreign.MemorySegment}, which - * models a contiguous region of memory, residing either inside or outside the Java heap. The contents of a memory - * segment can be described using a {@link java.lang.foreign.MemoryLayout memory layout}, which provides - * basic operations to query sizes, offsets and alignment constraints. Memory layouts also provide - * an alternate, more abstract way, to access memory segments - * using {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) var handles}, + * models a contiguous region of memory, residing either inside or outside the Java heap. Memory segments are + * typically allocated using an {@link java.lang.foreign.Arena}, which controls the lifetime of the regions of memory + * backing the segments it allocates. The contents of a memory segment can be described using a + * {@link java.lang.foreign.MemoryLayout memory layout}, which provides basic operations to query sizes, offsets and + * alignment constraints. Memory layouts also provide an alternate, more abstract way, to + * access memory segments using + * {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) var handles}, * which can be computed using layout paths. * * For example, to allocate an off-heap region of memory big enough to hold 10 values of the primitive type {@code int}, * and fill it with values ranging from {@code 0} to {@code 9}, we can use the following code: * * {@snippet lang = java: - * MemorySegment segment = MemorySegment.allocateNative(10 * 4, SegmentScope.auto()); - * for (int i = 0 ; i < 10 ; i++) { - * segment.setAtIndex(ValueLayout.JAVA_INT, i, i); - * } - *} - * - * This code creates a native memory segment, that is, a memory segment backed by - * off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}. - * The segment is associated with an {@linkplain java.lang.foreign.SegmentScope#auto() automatic scope}. This - * means that the off-heap region of memory backing the segment is managed, automatically, by the garbage collector. - * As such, the off-heap memory backing the native segment will be released at some unspecified - * point after the segment becomes unreachable. - * This is similar to what happens with direct buffers created via {@link java.nio.ByteBuffer#allocateDirect(int)}. - * It is also possible to manage the lifecycle of allocated native segments more directly, as shown in a later section. - *

      - * Inside a loop, we then initialize the contents of the memory segment; note how the - * {@linkplain java.lang.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) access method} - * accepts a {@linkplain java.lang.foreign.ValueLayout value layout}, which specifies the size, alignment constraint, - * byte order as well as the Java type ({@code int}, in this case) associated with the access operation. More specifically, - * if we view the memory segment as a set of 10 adjacent slots, {@code s[i]}, where {@code 0 <= i < 10}, - * where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot - * so that {@code s[i] = i}, again where {@code 0 <= i < 10}. - * - *

      Deterministic deallocation

      - * - * When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is - * often crucial that the resources associated with a memory segment are released when the segment is no longer in use, - * and in a timely fashion. For this reason, there might be cases where waiting for the garbage collector to determine that a segment - * is unreachable is not optimal. - * Clients that operate under these assumptions might want to programmatically release the memory backing a memory segment. - * This can be done, using the {@link java.lang.foreign.Arena} abstraction, as shown below: - * - * {@snippet lang = java: - * try (Arena arena = Arena.openConfined()) { + * try (Arena arena = Arena.ofConfined()) { * MemorySegment segment = arena.allocate(10 * 4); * for (int i = 0 ; i < 10 ; i++) { * segment.setAtIndex(ValueLayout.JAVA_INT, i, i); * } * } - *} + * } * - * This example is almost identical to the prior one; this time we first create an arena - * which is used to allocate multiple native segments which share the same life-cycle. That is, all the segments - * allocated by the arena will be associated with the same {@linkplain java.lang.foreign.SegmentScope scope}. - * Note the use of the try-with-resources construct: this idiom ensures that the off-heap region of memory backing the - * native segment will be released at the end of the block, according to the semantics described in Section {@jls 14.20.3} - * of The Java Language Specification. - * - *

      Safety

      - * - * This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment, + * This code creates a native memory segment, that is, a memory segment backed by + * off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}. + * The native segment is allocated using a {@linkplain java.lang.foreign.Arena#ofConfined() confined arena}. + * As such, access to the native segment is restricted to the current thread (the thread that created the arena). + * Moreover, when the arena is closed, the native segment is invalidated, and its backing region of memory is + * deallocated. Note the use of the try-with-resources construct: this idiom ensures that the off-heap region + * of memory backing the native segment will be released at the end of the block, according to the semantics described + * in Section {@jls 14.20.3} of The Java Language Specification. + *

      + * Memory segments provide strong safety guarantees when it comes to memory access. First, when accessing a memory segment, * the access coordinates are validated (upon access), to make sure that access does not occur at any address which resides * outside the boundaries of the memory segment used by the access operation. We call this guarantee spatial safety; * in other words, access to memory segments is bounds-checked, in the same way as array access is, as described in * Section {@jls 15.10.4} of The Java Language Specification. *

      - * Since memory segments created with an arena can become invalid (see above), segments are also validated (upon access) to make sure that - * the scope associated with the segment being accessed is still alive. - * We call this guarantee temporal safety. Together, spatial and temporal safety ensure that each memory access - * operation either succeeds - and accesses a valid location within the region of memory backing the memory segment - or fails. + * Additionally, to prevent a region of memory from being accessed after it has been deallocated + * (i.e. use-after-free), a segment is also validated (upon access) to make sure that the arena from which it + * has been obtained has not been closed. We call this guarantee temporal safety. + *

      + * Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid + * location within the region of memory backing the memory segment - or fails. * *

      Foreign function access

      * The key abstractions introduced to support foreign function access are {@link java.lang.foreign.SymbolLookup}, @@ -111,7 +82,7 @@ * so that clients can perform foreign function calls directly in Java, without the need for intermediate layers of C/C++ * code (as is the case with the Java Native Interface (JNI)). *

      - * For example, to compute the length of a string using the C standard library function {@code strlen} on a Linux x64 platform, + * For example, to compute the length of a string using the C standard library function {@code strlen} on a Linux/x64 platform, * we can use the following code: * * {@snippet lang = java: @@ -122,90 +93,39 @@ * FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS) * ); * - * try (Arena arena = Arena.openConfined()) { + * try (Arena arena = Arena.ofConfined()) { * MemorySegment cString = arena.allocateUtf8String("Hello"); - * long len = (long)strlen.invoke(cString); // 5 + * long len = (long)strlen.invokeExact(cString); // 5 * } *} * * Here, we obtain a {@linkplain java.lang.foreign.Linker#nativeLinker() native linker} and we use it - * to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} symbol in the - * standard C library; a downcall method handle targeting said symbol is subsequently + * to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} function in the + * standard C library; a downcall method handle targeting said function is subsequently * {@linkplain java.lang.foreign.Linker#downcallHandle(FunctionDescriptor, Linker.Option...) obtained}. * To complete the linking successfully, we must provide a {@link java.lang.foreign.FunctionDescriptor} instance, * describing the signature of the {@code strlen} function. * From this information, the linker will uniquely determine the sequence of steps which will turn - * the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invoke(java.lang.Object...)}) + * the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invokeExact(java.lang.Object...)}) * into a foreign function call, according to the rules specified by the ABI of the underlying platform. * The {@link java.lang.foreign.Arena} class also provides many useful methods for * interacting with foreign code, such as * {@linkplain java.lang.foreign.SegmentAllocator#allocateUtf8String(java.lang.String) converting} Java strings into * zero-terminated, UTF-8 strings, as demonstrated in the above example. * - *

      Upcalls

      - * The {@link java.lang.foreign.Linker} interface also allows clients to turn an existing method handle (which might point - * to a Java method) into a memory segment, so that Java code can effectively be passed to other foreign functions. - * For instance, we can write a method that compares two integer values, as follows: - * - * {@snippet lang=java : - * class IntComparator { - * static int intCompare(MemorySegment addr1, MemorySegment addr2) { - * return addr1.get(ValueLayout.JAVA_INT, 0) - - * addr2.get(ValueLayout.JAVA_INT, 0); - * - * } - * } - * } - * - * The above method accesses two foreign memory segments containing an integer value, and performs a simple comparison - * by returning the difference between such values. We can then obtain a method handle which targets the above static - * method, as follows: - * - * {@snippet lang = java: - * FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, - * ValueLayout.ADDRESS.asUnbounded(), - * ValueLayout.ADDRESS.asUnbounded()); - * MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class, - * "intCompare", - * intCompareDescriptor.toMethodType()); - *} - * - * As before, we need to create a {@link java.lang.foreign.FunctionDescriptor} instance, this time describing the signature - * of the function pointer we want to create. The descriptor can be used to - * {@linkplain java.lang.foreign.FunctionDescriptor#toMethodType() derive} a method type - * that can be used to look up the method handle for {@code IntComparator.intCompare}. - *

      - * Now that we have a method handle instance, we can turn it into a fresh function pointer, - * using the {@link java.lang.foreign.Linker} interface, as follows: - * - * {@snippet lang = java: - * SegmentScope scope = ... - * MemorySegment comparFunc = Linker.nativeLinker().upcallStub( - * intCompareHandle, intCompareDescriptor, scope); - * ); - *} - * - * The {@link java.lang.foreign.FunctionDescriptor} instance created in the previous step is then used to - * {@linkplain java.lang.foreign.Linker#upcallStub(java.lang.invoke.MethodHandle, FunctionDescriptor, SegmentScope) create} - * a new upcall stub; the layouts in the function descriptors allow the linker to determine the sequence of steps which - * allow foreign code to call the stub for {@code intCompareHandle} according to the rules specified by the ABI of the - * underlying platform. - * The lifecycle of the upcall stub is tied to the {@linkplain java.lang.foreign.SegmentScope scope} - * provided when the upcall stub is created. This same scope is made available by the {@link java.lang.foreign.MemorySegment} - * instance returned by that method. - * *

      Restricted methods

      * Some methods in this package are considered restricted. Restricted methods are typically used to bind native * foreign data and/or functions to first-class Java API elements which can then be used directly by clients. For instance - * the restricted method {@link java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)} - * can be used to create a fresh segment with the given spatial bounds out of a native address. + * the restricted method {@link java.lang.foreign.MemorySegment#reinterpret(long)} ()} + * can be used to create a fresh segment with the same address and temporal bounds, + * but with the provided size. This can be useful to resize memory segments obtained when interacting with native functions. *

      * Binding foreign data and/or functions is generally unsafe and, if done incorrectly, can result in VM crashes, - * or memory corruption when the bound Java API element is accessed. For instance, in the case of - * {@link java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)}, if the provided spatial bounds are - * incorrect, a client of the segment returned by that method might crash the VM, or corrupt - * memory when attempting to access said segment. For these reasons, it is crucial for code that calls a restricted method - * to never pass arguments that might cause incorrect binding of foreign data and/or functions to a Java API. + * or memory corruption when the bound Java API element is accessed. For instance, incorrectly resizing a native + * memory sgement using {@link java.lang.foreign.MemorySegment#reinterpret(long)} can lead to a JVM crash, or, worse, + * lead to silent memory corruption when attempting to access the resized segment. For these reasons, it is crucial for + * code that calls a restricted method to never pass arguments that might cause incorrect binding of foreign data and/or + * functions to a Java API. *

      * Given the potential danger of restricted methods, the Java runtime issues a warning on the standard error stream * every time a restricted method is invoked. Such warnings can be disabled by granting access to restricted methods diff --git a/src/java.base/share/classes/java/nio/Buffer.java b/src/java.base/share/classes/java/nio/Buffer.java index 843c9b3becd..3a2ba80ce55 100644 --- a/src/java.base/share/classes/java/nio/Buffer.java +++ b/src/java.base/share/classes/java/nio/Buffer.java @@ -815,7 +815,9 @@ public abstract sealed class Buffer @Override public ByteBuffer newMappedByteBuffer(UnmapperProxy unmapperProxy, long address, int cap, Object obj, MemorySegment segment) { - return new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment); + return unmapperProxy == null + ? new DirectByteBuffer(address, cap, obj, segment) + : new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment); } @Override diff --git a/src/java.base/share/classes/java/nio/channels/FileChannel.java b/src/java.base/share/classes/java/nio/channels/FileChannel.java index 741ad048cb7..c193f209cd2 100644 --- a/src/java.base/share/classes/java/nio/channels/FileChannel.java +++ b/src/java.base/share/classes/java/nio/channels/FileChannel.java @@ -26,8 +26,8 @@ package java.nio.channels; import java.io.IOException; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.spi.AbstractInterruptibleChannel; @@ -1004,11 +1004,15 @@ public abstract class FileChannel /** * Maps a region of this channel's file into a new mapped memory segment, - * with the given offset, size and memory session. + * with the given offset, size and arena. * The {@linkplain MemorySegment#address() address} of the returned memory * segment is the starting address of the mapped off-heap region backing * the segment. - * + *

      + * The lifetime of the returned segment is controlled by the provided arena. + * For instance, if the provided arena is a closeable arena, + * the returned segment will be unmapped when the provided closeable arena + * is {@linkplain Arena#close() closed}. *

      If the specified mapping mode is * {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, the resulting * segment will be read-only (see {@link MemorySegment#isReadOnly()}). @@ -1049,8 +1053,8 @@ public abstract class FileChannel * The size (in bytes) of the mapped memory backing the memory * segment. * - * @param session - * The segment memory session. + * @param arena + * The segment arena. * * @return A new mapped memory segment. * @@ -1059,13 +1063,11 @@ public abstract class FileChannel * {@code offset + size} overflows the range of {@code long}. * * @throws IllegalStateException - * If the {@code session} is not - * {@linkplain SegmentScope#isAlive() alive}. + * If {@code arena.isAlive() == false}. * * @throws WrongThreadException - * If this method is called from a thread other than the thread - * {@linkplain SegmentScope#isAccessibleBy(Thread) owning} the - * {@code session}. + * If {@code arena} is a confined scoped arena, and this method is called from a + * thread {@code T}, other than the scoped arena's owner thread. * * @throws NonReadableChannelException * If the {@code mode} is {@link MapMode#READ_ONLY READ_ONLY} or @@ -1087,7 +1089,7 @@ public abstract class FileChannel * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) - public MemorySegment map(MapMode mode, long offset, long size, SegmentScope session) + public MemorySegment map(MapMode mode, long offset, long size, Arena arena) throws IOException { throw new UnsupportedOperationException(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index 10dde1f331b..021aeed43c9 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,11 +25,7 @@ package jdk.internal.foreign; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; import java.lang.reflect.Array; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -47,11 +43,14 @@ import java.util.function.Function; import java.util.function.IntFunction; import java.util.stream.Stream; import java.util.stream.StreamSupport; + import jdk.internal.access.JavaNioAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.access.foreign.UnmapperProxy; import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.misc.Unsafe; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; import jdk.internal.util.ArraysSupport; import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; @@ -77,16 +76,16 @@ public abstract sealed class AbstractMemorySegmentImpl final long length; final boolean readOnly; - final SegmentScope scope; + final MemorySessionImpl scope; @ForceInline - AbstractMemorySegmentImpl(long length, boolean readOnly, SegmentScope scope) { + AbstractMemorySegmentImpl(long length, boolean readOnly, MemorySessionImpl scope) { this.length = length; this.readOnly = readOnly; this.scope = scope; } - abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope); + abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope); abstract ByteBuffer makeByteBuffer(); @@ -112,6 +111,50 @@ public abstract sealed class AbstractMemorySegmentImpl return asSliceNoCheck(offset, length - offset); } + @Override + public MemorySegment asSlice(long offset, long newSize, long byteAlignment) { + checkBounds(offset, newSize); + if (!isAlignedForElement(offset, byteAlignment)) { + throw new IllegalArgumentException("Target offset incompatible with alignment constraints"); + } + return asSliceNoCheck(offset, newSize); + } + + @Override + @CallerSensitive + public final MemorySegment reinterpret(long newSize, Arena arena, Consumer cleanup) { + Objects.requireNonNull(arena); + return reinterpretInternal(Reflection.getCallerClass(), newSize, + MemorySessionImpl.toMemorySession(arena), null); + } + + @Override + @CallerSensitive + public final MemorySegment reinterpret(long newSize) { + return reinterpretInternal(Reflection.getCallerClass(), newSize, scope, null); + } + + @Override + @CallerSensitive + public final MemorySegment reinterpret(Arena arena, Consumer cleanup) { + Objects.requireNonNull(arena); + return reinterpretInternal(Reflection.getCallerClass(), byteSize(), + MemorySessionImpl.toMemorySession(arena), cleanup); + } + + public MemorySegment reinterpretInternal(Class callerClass, long newSize, Scope scope, Consumer cleanup) { + Reflection.ensureNativeAccess(callerClass, MemorySegment.class, "reinterpret"); + if (newSize < 0) { + throw new IllegalArgumentException("newSize < 0"); + } + if (!isNative()) throw new UnsupportedOperationException("Not a native segment"); + Runnable action = cleanup != null ? + () -> cleanup.accept(NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize)) : + null; + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize, + (MemorySessionImpl)scope, action); + } + private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) { return dup(offset, newSize, readOnly, scope); } @@ -148,7 +191,7 @@ public abstract sealed class AbstractMemorySegmentImpl @Override public MemorySegment allocate(long byteSize, long byteAlignment) { Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); - return asSlice(0, byteSize); + return asSlice(0, byteSize, byteAlignment); } /** @@ -325,7 +368,12 @@ public abstract sealed class AbstractMemorySegmentImpl @ForceInline public final boolean isAlignedForElement(long offset, MemoryLayout layout) { - return (((unsafeGetOffset() + offset) | maxAlignMask()) & (layout.byteAlignment() - 1)) == 0; + return isAlignedForElement(offset, layout.byteAlignment()); + } + + @ForceInline + public final boolean isAlignedForElement(long offset, long byteAlignment) { + return (((unsafeGetOffset() + offset) | maxAlignMask()) & (byteAlignment - 1)) == 0; } private int checkArraySize(String typeName, int elemSize) { @@ -358,13 +406,18 @@ public abstract sealed class AbstractMemorySegmentImpl } @Override - public SegmentScope scope() { + public Scope scope() { return scope; } + @Override + public boolean isAccessibleBy(Thread thread) { + return sessionImpl().isAccessibleBy(thread); + } + @ForceInline public final MemorySessionImpl sessionImpl() { - return (MemorySessionImpl)scope; + return scope; } private IndexOutOfBoundsException outOfBoundException(long offset, long length) { @@ -450,7 +503,7 @@ public abstract sealed class AbstractMemorySegmentImpl @Override public String toString() { - return "MemorySegment{ array: " + array() + " address:" + address() + " limit: " + length + " }"; + return "MemorySegment{ heapBase: " + heapBase() + " address:" + address() + " limit: " + length + " }"; } @Override @@ -481,29 +534,29 @@ public abstract sealed class AbstractMemorySegmentImpl int size = limit - pos; AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl) NIO_ACCESS.bufferSegment(bb); - final SegmentScope bufferScope; + boolean readOnly = bb.isReadOnly(); + int scaleFactor = getScaleFactor(bb); + final MemorySessionImpl bufferScope; if (bufferSegment != null) { bufferScope = bufferSegment.scope; } else { bufferScope = MemorySessionImpl.heapSession(bb); } - boolean readOnly = bb.isReadOnly(); - int scaleFactor = getScaleFactor(bb); if (base != null) { if (base instanceof byte[]) { - return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly); + return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope); } else if (base instanceof short[]) { - return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly); + return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope); } else if (base instanceof char[]) { - return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly); + return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope); } else if (base instanceof int[]) { - return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly); + return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope); } else if (base instanceof float[]) { - return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly); + return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope); } else if (base instanceof long[]) { - return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly); + return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope); } else if (base instanceof double[]) { - return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly); + return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope); } else { throw new AssertionError("Cannot get here"); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/CABI.java b/src/java.base/share/classes/jdk/internal/foreign/CABI.java index a6dfba99533..afec17e92c0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/CABI.java +++ b/src/java.base/share/classes/jdk/internal/foreign/CABI.java @@ -25,10 +25,13 @@ */ package jdk.internal.foreign; +import jdk.internal.foreign.abi.fallback.FallbackLinker; +import jdk.internal.vm.ForeignLinkerSupport; import jdk.internal.util.OperatingSystem; import jdk.internal.util.StaticProperty; import static java.lang.foreign.ValueLayout.ADDRESS; +import static sun.security.action.GetPropertyAction.privilegedGetProperty; public enum CABI { SYS_V, @@ -36,51 +39,52 @@ public enum CABI { LINUX_AARCH_64, MAC_OS_AARCH_64, WIN_AARCH_64, - LINUX_RISCV_64; + LINUX_RISCV_64, + FALLBACK, + UNSUPPORTED; - private static final CABI ABI; - private static final String ARCH; - private static final long ADDRESS_SIZE; + private static final CABI CURRENT = computeCurrent(); - static { - ARCH = StaticProperty.osArch(); - ADDRESS_SIZE = ADDRESS.bitSize(); - // might be running in a 32-bit VM on a 64-bit platform. - // addressSize will be correctly 32 - if ((ARCH.equals("amd64") || ARCH.equals("x86_64")) && ADDRESS_SIZE == 64) { - if (OperatingSystem.isWindows()) { - ABI = WIN_64; - } else { - ABI = SYS_V; - } - } else if (ARCH.equals("aarch64")) { - if (OperatingSystem.isMacOS()) { - ABI = MAC_OS_AARCH_64; - } else if (OperatingSystem.isWindows()) { - ABI = WIN_AARCH_64; - } else { - // The Linux ABI follows the standard AAPCS ABI - ABI = LINUX_AARCH_64; - } - } else if (ARCH.equals("riscv64")) { - if (OperatingSystem.isLinux()) { - ABI = LINUX_RISCV_64; - } else { - // unsupported - ABI = null; - } - } else { - // unsupported - ABI = null; + private static CABI computeCurrent() { + String abi = privilegedGetProperty("jdk.internal.foreign.CABI"); + if (abi != null) { + return CABI.valueOf(abi); } + + if (ForeignLinkerSupport.isSupported()) { + // figure out the ABI based on the platform + String arch = StaticProperty.osArch(); + long addressSize = ADDRESS.bitSize(); + // might be running in a 32-bit VM on a 64-bit platform. + // addressSize will be correctly 32 + if ((arch.equals("amd64") || arch.equals("x86_64")) && addressSize == 64) { + if (OperatingSystem.isWindows()) { + return WIN_64; + } else { + return SYS_V; + } + } else if (arch.equals("aarch64")) { + if (OperatingSystem.isMacOS()) { + return MAC_OS_AARCH_64; + } else if (OperatingSystem.isWindows()) { + return WIN_AARCH_64; + } else { + // The Linux ABI follows the standard AAPCS ABI + return LINUX_AARCH_64; + } + } else if (arch.equals("riscv64")) { + if (OperatingSystem.isLinux()) { + return LINUX_RISCV_64; + } + } + } else if (FallbackLinker.isSupported()) { + return FALLBACK; // fallback linker + } + + return UNSUPPORTED; } public static CABI current() { - if (ABI == null) { - throw new UnsupportedOperationException( - "Unsupported os, arch, or address size: " + OperatingSystem.current() + - ", " + ARCH + ", " + ADDRESS_SIZE); - } - return ABI; + return CURRENT; } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java index 260c41e6907..67a00bc8e6d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -27,7 +27,6 @@ package jdk.internal.foreign; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.util.Objects; import java.util.Optional; @@ -46,7 +45,7 @@ import jdk.internal.vm.annotation.ForceInline; * the field type storing the 'base' coordinate is just Object; similarly, all the constructor in the subclasses * accept an Object 'base' parameter instead of a sharper type (e.g. {@code byte[]}). This is deliberate, as * using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations, - * such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, SegmentScope)}. + * such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, MemorySessionImpl)}. */ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl { @@ -62,13 +61,15 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment final Object base; @Override - public Optional array() { - return Optional.of(base); + public Optional heapBase() { + return readOnly ? + Optional.empty() : + Optional.of(base); } @ForceInline - HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly) { - super(length, readOnly, SegmentScope.global()); + HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(length, readOnly, session); this.offset = offset; this.base = base; } @@ -79,28 +80,28 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment } @Override - abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope); + abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope); @Override ByteBuffer makeByteBuffer() { - if (!(base instanceof byte[])) { + if (!(base instanceof byte[] baseByte)) { throw new UnsupportedOperationException("Not an address to an heap-allocated byte array"); } JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess(); - return nioAccess.newHeapByteBuffer((byte[])base, (int)offset - BYTE_ARR_BASE, (int) byteSize(), null); + return nioAccess.newHeapByteBuffer(baseByte, (int)offset - BYTE_ARR_BASE, (int) byteSize(), null); } // factories public static final class OfByte extends HeapMemorySegmentImpl { - OfByte(long offset, Object base, long length, boolean readOnly) { - super(offset, base, length, readOnly); + OfByte(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(offset, base, length, readOnly, session); } @Override - OfByte dup(long offset, long size, boolean readOnly, SegmentScope scope) { - return new OfByte(this.offset + offset, base, size, readOnly); + OfByte dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { + return new OfByte(this.offset + offset, base, size, readOnly, scope); } @Override @@ -111,7 +112,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static MemorySegment fromArray(byte[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE; - return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false); + return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); } @Override @@ -127,13 +129,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static final class OfChar extends HeapMemorySegmentImpl { - OfChar(long offset, Object base, long length, boolean readOnly) { - super(offset, base, length, readOnly); + OfChar(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(offset, base, length, readOnly, session); } @Override - OfChar dup(long offset, long size, boolean readOnly, SegmentScope scope) { - return new OfChar(this.offset + offset, base, size, readOnly); + OfChar dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { + return new OfChar(this.offset + offset, base, size, readOnly, scope); } @Override @@ -144,7 +146,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static MemorySegment fromArray(char[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE; - return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false); + return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); } @Override @@ -160,13 +163,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static final class OfShort extends HeapMemorySegmentImpl { - OfShort(long offset, Object base, long length, boolean readOnly) { - super(offset, base, length, readOnly); + OfShort(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(offset, base, length, readOnly, session); } @Override - OfShort dup(long offset, long size, boolean readOnly, SegmentScope scope) { - return new OfShort(this.offset + offset, base, size, readOnly); + OfShort dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { + return new OfShort(this.offset + offset, base, size, readOnly, scope); } @Override @@ -177,7 +180,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static MemorySegment fromArray(short[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE; - return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false); + return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); } @Override @@ -193,13 +197,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static final class OfInt extends HeapMemorySegmentImpl { - OfInt(long offset, Object base, long length, boolean readOnly) { - super(offset, base, length, readOnly); + OfInt(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(offset, base, length, readOnly, session); } @Override - OfInt dup(long offset, long size, boolean readOnly, SegmentScope scope) { - return new OfInt(this.offset + offset, base, size, readOnly); + OfInt dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { + return new OfInt(this.offset + offset, base, size, readOnly, scope); } @Override @@ -210,7 +214,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static MemorySegment fromArray(int[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE; - return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false); + return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); } @Override @@ -226,13 +231,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static final class OfLong extends HeapMemorySegmentImpl { - OfLong(long offset, Object base, long length, boolean readOnly) { - super(offset, base, length, readOnly); + OfLong(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(offset, base, length, readOnly, session); } @Override - OfLong dup(long offset, long size, boolean readOnly, SegmentScope scope) { - return new OfLong(this.offset + offset, base, size, readOnly); + OfLong dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { + return new OfLong(this.offset + offset, base, size, readOnly, scope); } @Override @@ -243,7 +248,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static MemorySegment fromArray(long[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE; - return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false); + return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); } @Override @@ -259,13 +265,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static final class OfFloat extends HeapMemorySegmentImpl { - OfFloat(long offset, Object base, long length, boolean readOnly) { - super(offset, base, length, readOnly); + OfFloat(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(offset, base, length, readOnly, session); } @Override - OfFloat dup(long offset, long size, boolean readOnly, SegmentScope scope) { - return new OfFloat(this.offset + offset, base, size, readOnly); + OfFloat dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { + return new OfFloat(this.offset + offset, base, size, readOnly, scope); } @Override @@ -276,7 +282,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static MemorySegment fromArray(float[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE; - return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false); + return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); } @Override @@ -292,13 +299,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static final class OfDouble extends HeapMemorySegmentImpl { - OfDouble(long offset, Object base, long length, boolean readOnly) { - super(offset, base, length, readOnly); + OfDouble(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) { + super(offset, base, length, readOnly, session); } @Override - OfDouble dup(long offset, long size, boolean readOnly, SegmentScope scope) { - return new OfDouble(this.offset + offset, base, size, readOnly); + OfDouble dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { + return new OfDouble(this.offset + offset, base, size, readOnly, scope); } @Override @@ -309,7 +316,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment public static MemorySegment fromArray(double[] arr) { Objects.requireNonNull(arr); long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE; - return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false); + return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); } @Override diff --git a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java index 8e1ae388bbb..936b4813847 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java +++ b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -27,6 +27,7 @@ package jdk.internal.foreign; import jdk.internal.vm.annotation.ForceInline; +import java.lang.foreign.AddressLayout; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; @@ -53,9 +54,11 @@ public class LayoutPath { private static final long[] EMPTY_STRIDES = new long[0]; private static final long[] EMPTY_BOUNDS = new long[0]; + private static final MethodHandle[] EMPTY_DEREF_HANDLES = new MethodHandle[0]; private static final MethodHandle MH_ADD_SCALED_OFFSET; private static final MethodHandle MH_SLICE; + private static final MethodHandle MH_SEGMENT_RESIZE; static { try { @@ -64,6 +67,8 @@ public class LayoutPath { MethodType.methodType(long.class, long.class, long.class, long.class, long.class)); MH_SLICE = lookup.findVirtual(MemorySegment.class, "asSlice", MethodType.methodType(MemorySegment.class, long.class, long.class)); + MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment", + MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class)); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } @@ -75,12 +80,14 @@ public class LayoutPath { private final long[] strides; private final long[] bounds; + private final MethodHandle[] derefAdapters; - private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, LayoutPath enclosing) { + private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath enclosing) { this.layout = layout; this.offset = offset; this.strides = strides; this.bounds = bounds; + this.derefAdapters = derefAdapters; this.enclosing = enclosing; } @@ -90,7 +97,7 @@ public class LayoutPath { check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout"); SequenceLayout seq = (SequenceLayout)layout; MemoryLayout elem = seq.elementLayout(); - return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), addBound(seq.elementCount()), this); + return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), addBound(seq.elementCount()), derefAdapters, this); } public LayoutPath sequenceElement(long start, long step) { @@ -104,7 +111,7 @@ public class LayoutPath { start + 1; long maxIndex = Math.ceilDiv(nelems, Math.abs(step)); return LayoutPath.nestedPath(elem, offset + (start * elemSize), - addStride(elemSize * step), addBound(maxIndex), this); + addStride(elemSize * step), addBound(maxIndex), derefAdapters, this); } public LayoutPath sequenceElement(long index) { @@ -113,7 +120,7 @@ public class LayoutPath { checkSequenceBounds(seq, index); long elemSize = seq.elementLayout().bitSize(); long elemOffset = elemSize * index; - return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, this); + return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters,this); } public LayoutPath groupElement(String name) { @@ -134,7 +141,41 @@ public class LayoutPath { if (elem == null) { throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout); } - return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, this); + return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this); + } + + public LayoutPath groupElement(long index) { + check(GroupLayout.class, "attempting to select a group element from a non-group layout"); + GroupLayout g = (GroupLayout)layout; + long elemSize = g.memberLayouts().size(); + long offset = 0; + MemoryLayout elem = null; + for (int i = 0; i <= index; i++) { + if (i == elemSize) { + throw badLayoutPath("cannot resolve element " + index + " in layout " + layout); + } + elem = g.memberLayouts().get(i); + if (g instanceof StructLayout && i < index) { + offset += elem.bitSize(); + } + } + return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this); + } + + public LayoutPath derefElement() { + if (!(layout instanceof AddressLayout addressLayout) || + addressLayout.targetLayout().isEmpty()) { + throw badLayoutPath("Cannot dereference layout: " + layout); + } + MemoryLayout derefLayout = addressLayout.targetLayout().get(); + MethodHandle handle = dereferenceHandle(false).toMethodHandle(VarHandle.AccessMode.GET); + handle = MethodHandles.filterReturnValue(handle, + MethodHandles.insertArguments(MH_SEGMENT_RESIZE, 1, derefLayout)); + return derefPath(derefLayout, handle, this); + } + + private static MemorySegment resizeSegment(MemorySegment segment, MemoryLayout layout) { + return Utils.longToAddress(segment.address(), layout.byteSize(), layout.byteAlignment()); } // Layout path projections @@ -144,22 +185,31 @@ public class LayoutPath { } public VarHandle dereferenceHandle() { + return dereferenceHandle(true); + } + + public VarHandle dereferenceHandle(boolean adapt) { if (!(layout instanceof ValueLayout valueLayout)) { throw new IllegalArgumentException("Path does not select a value layout"); } - checkAlignment(this); VarHandle handle = Utils.makeSegmentViewVarHandle(valueLayout); for (int i = strides.length - 1; i >= 0; i--) { MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, - Utils.bitsToBytesOrThrow(strides[i], IllegalArgumentException::new), + Utils.bitsToBytes(strides[i]), bounds[i]); // (J, ...) -> J to (J, J, ...) -> J // i.e. new coord is prefixed. Last coord will correspond to innermost layout handle = MethodHandles.collectCoordinates(handle, 1, collector); } handle = MethodHandles.insertCoordinates(handle, 1, - Utils.bitsToBytesOrThrow(offset, IllegalArgumentException::new)); + Utils.bitsToBytes(offset)); + + if (adapt) { + for (int i = derefAdapters.length; i > 0; i--) { + handle = MethodHandles.collectCoordinates(handle, 0, derefAdapters[i - 1]); + } + } return handle; } @@ -182,13 +232,8 @@ public class LayoutPath { } public MethodHandle sliceHandle() { - if (strides.length == 0) { - // trigger checks eagerly - Utils.bitsToBytesOrThrow(offset, Utils.BITS_TO_BYTES_THROW_OFFSET); - } - MethodHandle offsetHandle = offsetHandle(); // bit offset - offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET); // byte offset + offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.BITS_TO_BYTES); // byte offset MethodHandle sliceHandle = MH_SLICE; // (MS, long, long) -> MS sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout.byteSize()); // (MS, long) -> MS @@ -204,11 +249,17 @@ public class LayoutPath { // Layout path construction public static LayoutPath rootPath(MemoryLayout layout) { - return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, null); + return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, EMPTY_DEREF_HANDLES, null); } - private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, LayoutPath encl) { - return new LayoutPath(layout, offset, strides, bounds, encl); + private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath encl) { + return new LayoutPath(layout, offset, strides, bounds, derefAdapters, encl); + } + + private static LayoutPath derefPath(MemoryLayout layout, MethodHandle handle, LayoutPath encl) { + MethodHandle[] handles = Arrays.copyOf(encl.derefAdapters, encl.derefAdapters.length + 1); + handles[encl.derefAdapters.length] = handle; + return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, handles, null); } // Helper methods @@ -229,26 +280,6 @@ public class LayoutPath { return new IllegalArgumentException("Bad layout path: " + cause); } - private static void checkAlignment(LayoutPath path) { - MemoryLayout layout = path.layout; - long alignment = layout.bitAlignment(); - if (!Utils.isAligned(path.offset, alignment)) { - throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout); - } - for (long stride : path.strides) { - if (!Utils.isAligned(stride, alignment)) { - throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match stride " + stride); - } - } - LayoutPath encl = path.enclosing; - if (encl != null) { - if (encl.layout.bitAlignment() < alignment) { - throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout); - } - checkAlignment(encl); - } - } - private long[] addStride(long stride) { long[] newStrides = Arrays.copyOf(strides, strides.length + 1); newStrides[strides.length] = stride; @@ -271,7 +302,8 @@ public class LayoutPath { SEQUENCE_ELEMENT("unbound sequence element"), SEQUENCE_ELEMENT_INDEX("bound sequence element"), SEQUENCE_RANGE("sequence range"), - GROUP_ELEMENT("group element"); + GROUP_ELEMENT("group element"), + DEREF_ELEMENT("dereference element"); final String description; diff --git a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java index 8b7c799d7ad..0c9482d8d36 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,8 +25,6 @@ package jdk.internal.foreign; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import jdk.internal.access.foreign.UnmapperProxy; import jdk.internal.misc.ScopedMemoryAccess; @@ -37,13 +35,13 @@ import jdk.internal.misc.ScopedMemoryAccess; * memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial * in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}). */ -public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { +public final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { private final UnmapperProxy unmapper; static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); - public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, SegmentScope scope) { + public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, MemorySessionImpl scope) { super(min, length, readOnly, scope); this.unmapper = unmapper; } @@ -55,7 +53,7 @@ public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { } @Override - MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope) { + MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { return new MappedMemorySegmentImpl(min + offset, unmapper, size, readOnly, scope); } @@ -73,50 +71,26 @@ public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { // support for mapped segments - public MemorySegment segment() { - return MappedMemorySegmentImpl.this; - } - public void load() { - SCOPED_MEMORY_ACCESS.load(sessionImpl(), min, unmapper.isSync(), length); + if (unmapper != null) { + SCOPED_MEMORY_ACCESS.load(sessionImpl(), min, unmapper.isSync(), length); + } } public void unload() { - SCOPED_MEMORY_ACCESS.unload(sessionImpl(), min, unmapper.isSync(), length); + if (unmapper != null) { + SCOPED_MEMORY_ACCESS.unload(sessionImpl(), min, unmapper.isSync(), length); + } } public boolean isLoaded() { - return SCOPED_MEMORY_ACCESS.isLoaded(sessionImpl(), min, unmapper.isSync(), length); + return unmapper == null || SCOPED_MEMORY_ACCESS.isLoaded(sessionImpl(), min, unmapper.isSync(), length); } public void force() { - SCOPED_MEMORY_ACCESS.force(sessionImpl(), unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length); - } - - public static final class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl { - - public EmptyMappedMemorySegmentImpl(boolean readOnly, MemorySessionImpl session) { - super(0, null, 0, readOnly, session); - } - - @Override - public void load() { - // do nothing - } - - @Override - public void unload() { - // do nothing - } - - @Override - public boolean isLoaded() { - return true; - } - - @Override - public void force() { - // do nothing + if (unmapper != null) { + SCOPED_MEMORY_ACCESS.force(sessionImpl(), unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length); } } + } diff --git a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java index f9addfffaee..1d145c09351 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java @@ -26,10 +26,9 @@ package jdk.internal.foreign; -import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment.Scope; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.ref.Cleaner; @@ -51,7 +50,7 @@ import jdk.internal.vm.annotation.ForceInline; * access is possible when a session is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}). */ public abstract sealed class MemorySessionImpl - implements SegmentScope, SegmentAllocator + implements Scope permits ConfinedSession, GlobalSession, SharedSession { static final int OPEN = 0; static final int CLOSING = -1; @@ -80,7 +79,7 @@ public abstract sealed class MemorySessionImpl public Arena asArena() { return new Arena() { @Override - public SegmentScope scope() { + public Scope scope() { return MemorySessionImpl.this; } @@ -88,16 +87,20 @@ public abstract sealed class MemorySessionImpl public void close() { MemorySessionImpl.this.close(); } - - @Override - public boolean isCloseableBy(Thread thread) { - Objects.requireNonNull(thread); - return ownerThread() == null || // shared - ownerThread() == thread; - } }; } + @ForceInline + public static final MemorySessionImpl toMemorySession(Arena arena) { + return (MemorySessionImpl) arena.scope(); + } + + public final boolean isCloseableBy(Thread thread) { + Objects.requireNonNull(thread); + return isCloseable() && + (owner == null || owner == thread); + } + public void addCloseAction(Runnable runnable) { Objects.requireNonNull(runnable); addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable)); @@ -150,7 +153,6 @@ public abstract sealed class MemorySessionImpl return new ImplicitSession(cleaner); } - @Override public MemorySegment allocate(long byteSize, long byteAlignment) { Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); return NativeMemorySegmentImpl.makeNativeSegment(byteSize, byteAlignment, this); @@ -160,7 +162,6 @@ public abstract sealed class MemorySessionImpl public abstract void acquire0(); - @Override public void whileAlive(Runnable action) { Objects.requireNonNull(action); acquire0(); @@ -175,12 +176,6 @@ public abstract sealed class MemorySessionImpl return owner; } - public static boolean sameOwnerThread(SegmentScope scope1, SegmentScope scope2) { - return ((MemorySessionImpl) scope1).ownerThread() == - ((MemorySessionImpl) scope2).ownerThread(); - } - - @Override public final boolean isAccessibleBy(Thread thread) { Objects.requireNonNull(thread); return owner == null || owner == thread; @@ -225,6 +220,10 @@ public abstract sealed class MemorySessionImpl } } + public static final void checkValidState(MemorySegment segment) { + ((AbstractMemorySegmentImpl)segment).sessionImpl().checkValidState(); + } + @Override protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java index 6d0e532d2d0..0738b81202a 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -27,7 +27,6 @@ package jdk.internal.foreign; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.util.Optional; @@ -52,9 +51,24 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe final long min; @ForceInline - NativeMemorySegmentImpl(long min, long length, boolean readOnly, SegmentScope scope) { + NativeMemorySegmentImpl(long min, long length, boolean readOnly, MemorySessionImpl scope) { super(length, readOnly, scope); - this.min = min; + this.min = (Unsafe.getUnsafe().addressSize() == 4) + // On 32-bit systems, normalize the upper unused 32-bits to zero + ? min & 0x0000_0000_FFFF_FFFFL + // On 64-bit systems, all the bits are used + : min; + } + + /** + * This constructor should only be used when initializing {@link MemorySegment#NULL}. Note: because of the memory + * segment class hierarchy, it is possible to end up in a situation where this constructor is called + * when the static fields in this class are not yet initialized. + */ + @ForceInline + public NativeMemorySegmentImpl() { + super(0L, false, new GlobalSession(null)); + this.min = 0L; } @Override @@ -63,13 +77,13 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe } @Override - public Optional array() { + public Optional heapBase() { return Optional.empty(); } @ForceInline @Override - NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope) { + NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { return new NativeMemorySegmentImpl(min + offset, size, readOnly, scope); } @@ -101,8 +115,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe // factories - public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, SegmentScope scope) { - MemorySessionImpl sessionImpl = (MemorySessionImpl) scope; + public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl) { sessionImpl.checkValidState(); if (VM.isDirectMemoryPageAligned()) { byteAlignment = Math.max(byteAlignment, NIO_ACCESS.pageSize()); @@ -119,7 +132,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe } long alignedBuf = Utils.alignUp(buf, byteAlignment); AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize, - false, scope); + false, sessionImpl); sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { @Override public void cleanup() { @@ -138,25 +151,23 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe // associated with MemorySegment::ofAddress. @ForceInline - public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope scope, Runnable action) { - MemorySessionImpl sessionImpl = (MemorySessionImpl) scope; + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl, Runnable action) { if (action == null) { sessionImpl.checkValidState(); } else { sessionImpl.addCloseAction(action); } - return new NativeMemorySegmentImpl(min, byteSize, false, scope); + return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl); } @ForceInline - public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope scope) { - MemorySessionImpl sessionImpl = (MemorySessionImpl) scope; + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl) { sessionImpl.checkValidState(); - return new NativeMemorySegmentImpl(min, byteSize, false, scope); + return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl); } @ForceInline public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) { - return new NativeMemorySegmentImpl(min, byteSize, false, SegmentScope.global()); + return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null)); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java index 435381a3021..36f8551f448 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -43,14 +43,14 @@ public final class SlicingAllocator implements SegmentAllocator { MemorySegment trySlice(long byteSize, long byteAlignment) { long min = segment.address(); long start = Utils.alignUp(min + sp, byteAlignment) - min; - MemorySegment slice = segment.asSlice(start, byteSize); + MemorySegment slice = segment.asSlice(start, byteSize, byteAlignment); sp = start + byteSize; return slice; } @Override public MemorySegment allocate(long byteSize, long byteAlignment) { - Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment, maxAlign); + Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); // try to slice from current segment first... return trySlice(byteSize, byteAlignment); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java index b4681e0814a..94b9b2e6a23 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -25,9 +25,7 @@ package jdk.internal.foreign; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SymbolLookup; +import java.lang.foreign.*; import java.lang.invoke.MethodHandles; import java.nio.file.Files; import java.nio.file.Path; @@ -47,7 +45,10 @@ public final class SystemLookup implements SymbolLookup { private static final SystemLookup INSTANCE = new SystemLookup(); /* A fallback lookup, used when creation of system lookup fails. */ - private static final SymbolLookup FALLBACK_LOOKUP = name -> Optional.empty(); + private static final SymbolLookup FALLBACK_LOOKUP = name -> { + Objects.requireNonNull(name); + return Optional.empty(); + }; /* * On POSIX systems, dlsym will allow us to lookup symbol in library dependencies; the same trick doesn't work @@ -57,10 +58,11 @@ public final class SystemLookup implements SymbolLookup { private static SymbolLookup makeSystemLookup() { try { - return switch (CABI.current()) { - case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); - case WIN_64, WIN_AARCH_64 -> makeWindowsLookup(); // out of line to workaround javac crash - }; + if (Utils.IS_WINDOWS) { + return makeWindowsLookup(); + } else { + return libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); + } } catch (Throwable ex) { // This can happen in the event of a library loading failure - e.g. if one of the libraries the // system lookup depends on cannot be loaded for some reason. In such extreme cases, rather than @@ -84,15 +86,17 @@ public final class SystemLookup implements SymbolLookup { SymbolLookup fallbackLibLookup = libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); - int numSymbols = WindowsFallbackSymbols.values().length; - MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.find("funcs").orElseThrow().address(), - ADDRESS.byteSize() * numSymbols, SegmentScope.global()); + MemorySegment funcs = fallbackLibLookup.find("funcs").orElseThrow() + .reinterpret(WindowsFallbackSymbols.LAYOUT.byteSize()); Function> fallbackLookup = name -> Optional.ofNullable(WindowsFallbackSymbols.valueOfOrNull(name)) - .map(symbol -> MemorySegment.ofAddress(funcs.getAtIndex(ADDRESS, symbol.ordinal()).address(), 0L, SegmentScope.global())); + .map(symbol -> funcs.getAtIndex(ADDRESS, symbol.ordinal())); final SymbolLookup finalLookup = lookup; - lookup = name -> finalLookup.find(name).or(() -> fallbackLookup.apply(name)); + lookup = name -> { + Objects.requireNonNull(name); + return finalLookup.find(name).or(() -> fallbackLookup.apply(name)); + }; } return lookup; @@ -106,7 +110,7 @@ public final class SystemLookup implements SymbolLookup { long addr = lib.lookup(name); return addr == 0 ? Optional.empty() : - Optional.of(MemorySegment.ofAddress(addr, 0, SegmentScope.global())); + Optional.of(MemorySegment.ofAddress(addr)); } catch (NoSuchMethodException e) { return Optional.empty(); } @@ -118,10 +122,7 @@ public final class SystemLookup implements SymbolLookup { */ private static Path jdkLibraryPath(String name) { Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home")); - String lib = switch (CABI.current()) { - case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> "lib"; - case WIN_64, WIN_AARCH_64 -> "bin"; - }; + String lib = Utils.IS_WINDOWS ? "bin" : "lib"; String libname = System.mapLibraryName(name); return javahome.resolve(lib).resolve(libname); } @@ -202,5 +203,8 @@ public final class SystemLookup implements SymbolLookup { return null; } } + + static final SequenceLayout LAYOUT = MemoryLayout.sequenceLayout( + values().length, ADDRESS); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/Utils.java b/src/java.base/share/classes/jdk/internal/foreign/Utils.java index 90df9393489..e6eda5130ec 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Utils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -26,6 +26,7 @@ package jdk.internal.foreign; +import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; @@ -39,27 +40,30 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; + import jdk.internal.access.SharedSecrets; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.vm.annotation.ForceInline; import sun.invoke.util.Wrapper; import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static sun.security.action.GetPropertyAction.privilegedGetProperty; /** * This class contains misc helper functions to support creation of memory segments. */ public final class Utils { + + public static final boolean IS_WINDOWS = privilegedGetProperty("os.name").startsWith("Windows"); + + // Suppresses default constructor, ensuring non-instantiability. + private Utils() {} + private static final MethodHandle BYTE_TO_BOOL; private static final MethodHandle BOOL_TO_BYTE; private static final MethodHandle ADDRESS_TO_LONG; - private static final MethodHandle LONG_TO_ADDRESS_SAFE; - private static final MethodHandle LONG_TO_ADDRESS_UNSAFE; - public static final MethodHandle MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET; - - public static final Supplier BITS_TO_BYTES_THROW_OFFSET - = () -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8"); + private static final MethodHandle LONG_TO_ADDRESS; + public static final MethodHandle BITS_TO_BYTES; static { try { @@ -70,15 +74,10 @@ public final class Utils { MethodType.methodType(byte.class, boolean.class)); ADDRESS_TO_LONG = lookup.findStatic(SharedUtils.class, "unboxSegment", MethodType.methodType(long.class, MemorySegment.class)); - LONG_TO_ADDRESS_SAFE = lookup.findStatic(Utils.class, "longToAddressSafe", - MethodType.methodType(MemorySegment.class, long.class)); - LONG_TO_ADDRESS_UNSAFE = lookup.findStatic(Utils.class, "longToAddressUnsafe", - MethodType.methodType(MemorySegment.class, long.class)); - MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET = MethodHandles.insertArguments( - lookup.findStatic(Utils.class, "bitsToBytesOrThrow", - MethodType.methodType(long.class, long.class, Supplier.class)), - 1, - BITS_TO_BYTES_THROW_OFFSET); + LONG_TO_ADDRESS = lookup.findStatic(Utils.class, "longToAddress", + MethodType.methodType(MemorySegment.class, long.class, long.class, long.class)); + BITS_TO_BYTES = lookup.findStatic(Utils.class, "bitsToBytes", + MethodType.methodType(long.class, long.class)); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } @@ -93,28 +92,25 @@ public final class Utils { return ms.asSlice(alignUp(offset, alignment) - offset); } - public static long bitsToBytesOrThrow(long bits, Supplier exFactory) { - if (Utils.isAligned(bits, 8)) { - return bits / 8; - } else { - throw exFactory.get(); - } + public static long bitsToBytes(long bits) { + assert Utils.isAligned(bits, 8); + return bits / Byte.SIZE; } public static VarHandle makeSegmentViewVarHandle(ValueLayout layout) { - class VarHandleCache { - private static final Map handleMap = new ConcurrentHashMap<>(); + final class VarHandleCache { + private static final Map HANDLE_MAP = new ConcurrentHashMap<>(); static VarHandle put(ValueLayout layout, VarHandle handle) { - VarHandle prev = handleMap.putIfAbsent(layout, handle); + VarHandle prev = HANDLE_MAP.putIfAbsent(layout, handle); return prev != null ? prev : handle; } } Class baseCarrier = layout.carrier(); if (layout.carrier() == MemorySegment.class) { baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) { - case 8 -> long.class; - case 4 -> int.class; + case Long.BYTES -> long.class; + case Integer.BYTES -> int.class; default -> throw new UnsupportedOperationException("Unsupported address layout"); }; } else if (layout.carrier() == boolean.class) { @@ -126,11 +122,12 @@ public final class Utils { if (layout.carrier() == boolean.class) { handle = MethodHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL); - } else if (layout instanceof ValueLayout.OfAddress addressLayout) { + } else if (layout instanceof AddressLayout addressLayout) { handle = MethodHandles.filterValue(handle, MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemorySegment.class)), - MethodHandles.explicitCastArguments(addressLayout.isUnbounded() ? - LONG_TO_ADDRESS_UNSAFE : LONG_TO_ADDRESS_SAFE, MethodType.methodType(MemorySegment.class, baseCarrier))); + MethodHandles.explicitCastArguments(MethodHandles.insertArguments(LONG_TO_ADDRESS, 1, + pointeeByteSize(addressLayout), pointeeByteAlign(addressLayout)), + MethodType.methodType(MemorySegment.class, baseCarrier))); } return VarHandleCache.put(layout, handle); } @@ -144,13 +141,19 @@ public final class Utils { } @ForceInline - private static MemorySegment longToAddressSafe(long addr) { - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, 0); + public static MemorySegment longToAddress(long addr, long size, long align) { + if (!isAligned(addr, align)) { + throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr); + } + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size); } @ForceInline - private static MemorySegment longToAddressUnsafe(long addr) { - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, Long.MAX_VALUE); + public static MemorySegment longToAddress(long addr, long size, long align, MemorySessionImpl scope) { + if (!isAligned(addr, align)) { + throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr); + } + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope); } public static void copy(MemorySegment addr, byte[] bytes) { @@ -172,24 +175,21 @@ public final class Utils { @ForceInline public static void checkElementAlignment(MemoryLayout layout, String msg) { - if (layout.bitAlignment() > layout.bitSize()) { + if (layout.byteAlignment() > layout.byteSize()) { throw new IllegalArgumentException(msg); } } - public static long pointeeSize(MemoryLayout layout) { - if (layout instanceof ValueLayout.OfAddress addressLayout) { - return addressLayout.isUnbounded() ? Long.MAX_VALUE : 0L; - } else { - throw new UnsupportedOperationException(); - } + public static long pointeeByteSize(AddressLayout addressLayout) { + return addressLayout.targetLayout() + .map(MemoryLayout::byteSize) + .orElse(0L); } - public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment, long maxAlignment) { - checkAllocationSizeAndAlign(byteSize, byteAlignment); - if (maxAlignment != 0 && byteAlignment > maxAlignment) { - throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment + " > " + maxAlignment); - } + public static long pointeeByteAlign(AddressLayout addressLayout) { + return addressLayout.targetLayout() + .map(MemoryLayout::byteAlignment) + .orElse(1L); } public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index aac62916df5..0744435b98d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -28,6 +28,7 @@ import jdk.internal.foreign.SystemLookup; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker; +import jdk.internal.foreign.abi.fallback.FallbackLinker; import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; @@ -35,7 +36,7 @@ import jdk.internal.foreign.layout.AbstractLayout; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; import java.lang.foreign.MemorySegment; @@ -46,15 +47,16 @@ import java.util.Objects; public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker, SysVx64Linker, WindowsAArch64Linker, - Windowsx64Linker, LinuxRISCV64Linker { + Windowsx64Linker, LinuxRISCV64Linker, + FallbackLinker { public interface UpcallStubFactory { - MemorySegment makeStub(MethodHandle target, SegmentScope arena); + MemorySegment makeStub(MethodHandle target, Arena arena); } private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {} private final SoftReferenceCache DOWNCALL_CACHE = new SoftReferenceCache<>(); - private final SoftReferenceCache UPCALL_CACHE = new SoftReferenceCache<>(); + private final SoftReferenceCache UPCALL_CACHE = new SoftReferenceCache<>(); @Override public MethodHandle downcallHandle(FunctionDescriptor function, Option... options) { @@ -74,23 +76,25 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options); @Override - public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope) { - Objects.requireNonNull(scope); + public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) { + Objects.requireNonNull(arena); Objects.requireNonNull(target); Objects.requireNonNull(function); checkHasNaturalAlignment(function); SharedUtils.checkExceptions(target); + LinkerOptions optionSet = LinkerOptions.forUpcall(function, options); MethodType type = function.toMethodType(); if (!type.equals(target.type())) { throw new IllegalArgumentException("Wrong method handle type: " + target.type()); } - UpcallStubFactory factory = UPCALL_CACHE.get(function, f -> arrangeUpcall(type, f)); - return factory.makeStub(target, scope); + UpcallStubFactory factory = UPCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> + arrangeUpcall(type, linkRequest.descriptor(), linkRequest.options())); + return factory.makeStub(target, arena); } - protected abstract UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function); + protected abstract UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options); @Override public SystemLookup defaultLookup() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java b/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java index f8bcd502b4e..008c6683528 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java @@ -24,14 +24,11 @@ */ package jdk.internal.foreign.abi; -import jdk.internal.foreign.NativeMemorySegmentImpl; import jdk.internal.foreign.Utils; +import jdk.internal.foreign.abi.BindingInterpreter.LoadFunc; +import jdk.internal.foreign.abi.BindingInterpreter.StoreFunc; -import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -199,103 +196,10 @@ import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED; */ public sealed interface Binding { - /** - * A binding context is used as an helper to carry out evaluation of certain bindings; for instance, - * it helps {@link Allocate} bindings, by providing the {@link SegmentAllocator} that should be used for - * the allocation operation, or {@link BoxAddress} bindings, by providing the {@link SegmentScope} that - * should be used to create an unsafe struct from a memory address. - */ - class Context implements AutoCloseable { - private final SegmentAllocator allocator; - private final SegmentScope scope; - - private Context(SegmentAllocator allocator, SegmentScope scope) { - this.allocator = allocator; - this.scope = scope; - } - - public SegmentAllocator allocator() { - return allocator; - } - - public SegmentScope scope() { - return scope; - } - - @Override - public void close() { - throw new UnsupportedOperationException(); - } - - /** - * Create a binding context from given native scope. - */ - public static Context ofBoundedAllocator(long size) { - Arena arena = Arena.openConfined(); - return new Context(SegmentAllocator.slicingAllocator(MemorySegment.allocateNative(size, arena.scope())), arena.scope()) { - @Override - public void close() { - arena.close(); - } - }; - } - - /** - * Create a binding context from given segment allocator. The resulting context will throw when - * the context's scope is accessed. - */ - public static Context ofAllocator(SegmentAllocator allocator) { - return new Context(allocator, null) { - @Override - public SegmentScope scope() { - throw new UnsupportedOperationException(); - } - }; - } - - /** - * Create a binding context from given scope. The resulting context will throw when - * the context's allocator is accessed. - */ - public static Context ofScope() { - Arena arena = Arena.openConfined(); - return new Context(null, arena.scope()) { - @Override - public SegmentAllocator allocator() { throw new UnsupportedOperationException(); } - - @Override - public void close() { - arena.close(); - } - }; - } - - /** - * Dummy binding context. Throws exceptions when attempting to access scope, return a throwing allocator, and has - * an idempotent {@link #close()}. - */ - public static final Context DUMMY = new Context(null, null) { - @Override - public SegmentAllocator allocator() { - return SharedUtils.THROWING_ALLOCATOR; - } - - @Override - public SegmentScope scope() { - throw new UnsupportedOperationException(); - } - - @Override - public void close() { - // do nothing - } - }; - } - void verify(Deque> stack); - void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context); + void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator); private static void checkType(Class type) { if (!type.isPrimitive() || type == void.class) @@ -352,16 +256,16 @@ public sealed interface Binding { return new Allocate(layout.byteSize(), layout.byteAlignment()); } - static BoxAddress boxAddressRaw(long size) { - return new BoxAddress(size, false); + static BoxAddress boxAddressRaw(long size, long align) { + return new BoxAddress(size, align, false); } static BoxAddress boxAddress(MemoryLayout layout) { - return new BoxAddress(layout.byteSize(), true); + return new BoxAddress(layout.byteSize(), layout.byteAlignment(), true); } static BoxAddress boxAddress(long byteSize) { - return new BoxAddress(byteSize, true); + return new BoxAddress(byteSize, 1, true); } static UnboxAddress unboxAddress() { @@ -463,8 +367,8 @@ public sealed interface Binding { return this; } - public Binding.Builder boxAddressRaw(long size) { - bindings.add(Binding.boxAddressRaw(size)); + public Binding.Builder boxAddressRaw(long size, long align) { + bindings.add(Binding.boxAddressRaw(size, align)); return this; } @@ -508,8 +412,8 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { storeFunc.store(storage(), type(), stack.pop()); } } @@ -527,8 +431,8 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { stack.push(loadFunc.load(storage(), type())); } } @@ -555,8 +459,8 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { Object value = stack.pop(); MemorySegment writeAddress = (MemorySegment) stack.pop(); if (SharedUtils.isPowerOfTwo(byteWidth())) { @@ -612,8 +516,8 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { MemorySegment readAddress = (MemorySegment) stack.pop(); if (SharedUtils.isPowerOfTwo(byteWidth())) { // exact size match @@ -657,8 +561,8 @@ public sealed interface Binding { * and pushes the new buffer onto the operand stack */ record Copy(long size, long alignment) implements Binding { - private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, Context context) { - return context.allocator().allocate(size, alignment) + private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, SegmentAllocator allocator) { + return allocator.allocate(size, alignment) .copyFrom(operand.asSlice(0, size)); } @@ -670,10 +574,10 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { MemorySegment operand = (MemorySegment) stack.pop(); - MemorySegment copy = copyBuffer(operand, size, alignment, context); + MemorySegment copy = copyBuffer(operand, size, alignment, allocator); stack.push(copy); } } @@ -683,8 +587,8 @@ public sealed interface Binding { * Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack. */ record Allocate(long size, long alignment) implements Binding { - private static MemorySegment allocateBuffer(long size, long alignment, Context context) { - return context.allocator().allocate(size, alignment); + private static MemorySegment allocateBuffer(long size, long alignment, SegmentAllocator allocator) { + return allocator.allocate(size, alignment); } @Override @@ -693,9 +597,9 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { - stack.push(allocateBuffer(size, alignment, context)); + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { + stack.push(allocateBuffer(size, alignment, allocator)); } } @@ -715,9 +619,9 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { - stack.push(((MemorySegment)stack.pop()).address()); + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { + stack.push(SharedUtils.unboxSegment((MemorySegment)stack.pop())); } } @@ -726,7 +630,7 @@ public sealed interface Binding { * Pops a 'long' from the operand stack, converts it to a 'MemorySegment', with the given size and memory scope * (either the context scope, or the global scope), and pushes that onto the operand stack. */ - record BoxAddress(long size, boolean needsScope) implements Binding { + record BoxAddress(long size, long align, boolean needsScope) implements Binding { @Override public void verify(Deque> stack) { @@ -736,11 +640,13 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { - SegmentScope scope = needsScope ? - context.scope() : SegmentScope.global(); - stack.push(NativeMemorySegmentImpl.makeNativeSegmentUnchecked((long) stack.pop(), size, scope)); + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { + MemorySegment segment = Utils.longToAddress((long) stack.pop(), size, align); + if (needsScope) { + segment = segment.reinterpret((Arena) allocator, null); + } + stack.push(segment); } } @@ -758,8 +664,8 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { stack.push(stack.peekLast()); } } @@ -773,8 +679,8 @@ public sealed interface Binding { enum Cast implements Binding { INT_TO_BOOLEAN(int.class, boolean.class) { @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { // implement least significant byte non-zero test int arg = (int) stack.pop(); boolean result = Utils.byteToBoolean((byte) arg); @@ -813,8 +719,8 @@ public sealed interface Binding { } @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { + public void interpret(Deque stack, StoreFunc storeFunc, + LoadFunc loadFunc, SegmentAllocator allocator) { Object arg = stack.pop(); MethodHandle converter = MethodHandles.explicitCastArguments(MethodHandles.identity(toType), MethodType.methodType(toType, fromType)); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java index 3a29c5fbc01..bc0e2b0c5d6 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -24,25 +24,26 @@ */ package jdk.internal.foreign.abi; +import java.lang.foreign.SegmentAllocator; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; public class BindingInterpreter { - static void unbox(Object arg, List bindings, StoreFunc storeFunc, Binding.Context context) { + static void unbox(Object arg, List bindings, StoreFunc storeFunc, SegmentAllocator allocator) { Deque stack = new ArrayDeque<>(); stack.push(arg); for (Binding b : bindings) { - b.interpret(stack, storeFunc, null, context); + b.interpret(stack, storeFunc, null, allocator); } } - static Object box(List bindings, LoadFunc loadFunc, Binding.Context context) { + static Object box(List bindings, LoadFunc loadFunc, SegmentAllocator allocator) { Deque stack = new ArrayDeque<>(); for (Binding b : bindings) { - b.interpret(stack, null, loadFunc, context); + b.interpret(stack, null, loadFunc, allocator); } return stack.pop(); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java index 6a931616a38..d26fdf6120d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java @@ -26,7 +26,6 @@ package jdk.internal.foreign.abi; import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.NativeMemorySegmentImpl; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.Binding.Allocate; import jdk.internal.foreign.abi.Binding.BoxAddress; @@ -53,12 +52,7 @@ import sun.security.action.GetPropertyAction; import java.io.IOException; import java.io.PrintWriter; import java.lang.constant.ConstantDescs; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -88,17 +82,16 @@ public class BindingSpecializer { private static final String VOID_DESC = methodType(void.class).descriptorString(); - private static final String BINDING_CONTEXT_DESC = Binding.Context.class.descriptorString(); - private static final String OF_BOUNDED_ALLOCATOR_DESC = methodType(Binding.Context.class, long.class).descriptorString(); - private static final String OF_SCOPE_DESC = methodType(Binding.Context.class).descriptorString(); - private static final String ALLOCATOR_DESC = methodType(SegmentAllocator.class).descriptorString(); - private static final String SCOPE_DESC = methodType(SegmentScope.class).descriptorString(); + private static final String ARENA_DESC = Arena.class.descriptorString(); + private static final String NEW_BOUNDED_ARENA_DESC = methodType(Arena.class, long.class).descriptorString(); + private static final String NEW_EMPTY_ARENA_DESC = methodType(Arena.class).descriptorString(); + private static final String SCOPE_DESC = methodType(MemorySegment.Scope.class).descriptorString(); private static final String SESSION_IMPL_DESC = methodType(MemorySessionImpl.class).descriptorString(); private static final String CLOSE_DESC = VOID_DESC; private static final String UNBOX_SEGMENT_DESC = methodType(long.class, MemorySegment.class).descriptorString(); private static final String COPY_DESC = methodType(void.class, MemorySegment.class, long.class, MemorySegment.class, long.class, long.class).descriptorString(); - private static final String OF_LONG_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString(); - private static final String OF_LONG_UNCHECKED_DESC = methodType(MemorySegment.class, long.class, long.class, SegmentScope.class).descriptorString(); + private static final String LONG_TO_ADDRESS_NO_SCOPE_DESC = methodType(MemorySegment.class, long.class, long.class, long.class).descriptorString(); + private static final String LONG_TO_ADDRESS_SCOPE_DESC = methodType(MemorySegment.class, long.class, long.class, long.class, MemorySessionImpl.class).descriptorString(); private static final String ALLOCATE_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString(); private static final String HANDLE_UNCAUGHT_EXCEPTION_DESC = methodType(void.class, Throwable.class).descriptorString(); private static final String METHOD_HANDLES_INTRN = Type.getInternalName(MethodHandles.class); @@ -165,7 +158,8 @@ public class BindingSpecializer { byte[] bytes = specializeHelper(leafHandle.type(), callerMethodType, callingSequence, abi); try { - MethodHandles.Lookup definedClassLookup = MethodHandles.lookup().defineHiddenClassWithClassData(bytes, leafHandle, false); + MethodHandles.Lookup definedClassLookup = MethodHandles.lookup() + .defineHiddenClassWithClassData(bytes, leafHandle, false); return definedClassLookup.findStatic(definedClassLookup.lookupClass(), METHOD_NAME, callerMethodType); } catch (IllegalAccessException | NoSuchMethodException e) { throw new InternalError("Should not happen", e); @@ -294,11 +288,11 @@ public class BindingSpecializer { // create a Binding.Context for this call if (callingSequence.allocationSize() != 0) { emitConst(callingSequence.allocationSize()); - emitInvokeStatic(Binding.Context.class, "ofBoundedAllocator", OF_BOUNDED_ALLOCATOR_DESC); + emitInvokeStatic(SharedUtils.class, "newBoundedArena", NEW_BOUNDED_ARENA_DESC); } else if (callingSequence.forUpcall() && needsSession()) { - emitInvokeStatic(Binding.Context.class, "ofScope", OF_SCOPE_DESC); + emitInvokeStatic(SharedUtils.class, "newEmptyArena", NEW_EMPTY_ARENA_DESC); } else { - emitGetStatic(Binding.Context.class, "DUMMY", BINDING_CONTEXT_DESC); + emitGetStatic(SharedUtils.class, "DUMMY_ARENA", ARENA_DESC); } contextIdx = newLocal(Object.class); emitStore(Object.class, contextIdx); @@ -422,13 +416,13 @@ public class BindingSpecializer { if (callingSequence.forDowncall()) { mv.visitInsn(ATHROW); } else { - emitInvokeStatic(SharedUtils.class, "handleUncaughtException", HANDLE_UNCAUGHT_EXCEPTION_DESC); - if (callerMethodType.returnType() != void.class) { - emitConstZero(callerMethodType.returnType()); - emitReturn(callerMethodType.returnType()); - } else { - mv.visitInsn(RETURN); - } + emitInvokeStatic(SharedUtils.class, "handleUncaughtException", HANDLE_UNCAUGHT_EXCEPTION_DESC); + if (callerMethodType.returnType() != void.class) { + emitConstZero(callerMethodType.returnType()); + emitReturn(callerMethodType.returnType()); + } else { + mv.visitInsn(RETURN); + } } mv.visitTryCatchBlock(tryStart, tryEnd, catchStart, null); @@ -454,7 +448,7 @@ public class BindingSpecializer { .get(paramIndex - offset); // is this an address layout? - return paramLayout instanceof ValueLayout.OfAddress; + return paramLayout instanceof AddressLayout; } private void emitCleanup() { @@ -563,29 +557,32 @@ public class BindingSpecializer { private void emitLoadInternalSession() { assert contextIdx != -1; emitLoad(Object.class, contextIdx); - emitInvokeVirtual(Binding.Context.class, "scope", SCOPE_DESC); + emitCheckCast(Arena.class); + emitInvokeInterface(Arena.class, "scope", SCOPE_DESC); + emitCheckCast(MemorySessionImpl.class); } private void emitLoadInternalAllocator() { assert contextIdx != -1; emitLoad(Object.class, contextIdx); - emitInvokeVirtual(Binding.Context.class, "allocator", ALLOCATOR_DESC); } private void emitCloseContext() { assert contextIdx != -1; emitLoad(Object.class, contextIdx); - emitInvokeVirtual(Binding.Context.class, "close", CLOSE_DESC); + emitCheckCast(Arena.class); + emitInvokeInterface(Arena.class, "close", CLOSE_DESC); } private void emitBoxAddress(BoxAddress boxAddress) { popType(long.class); emitConst(boxAddress.size()); + emitConst(boxAddress.align()); if (needsSession()) { emitLoadInternalSession(); - emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_UNCHECKED_DESC); + emitInvokeStatic(Utils.class, "longToAddress", LONG_TO_ADDRESS_SCOPE_DESC); } else { - emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_DESC); + emitInvokeStatic(Utils.class, "longToAddress", LONG_TO_ADDRESS_NO_SCOPE_DESC); } pushType(MemorySegment.class); } @@ -934,7 +931,7 @@ public class BindingSpecializer { } else if (type == double.class) { return ValueLayout.OfDouble.class; } else if (type == MemorySegment.class) { - return ValueLayout.OfAddress.class; + return AddressLayout.class; } else { throw new IllegalStateException("Unknown type: " + type); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java index cd56fbe548f..fe1ac10b518 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -191,6 +191,10 @@ public class CallingSequence { .reduce(0, (a, b) -> a | b); } + public boolean needsTransition() { + return !linkerOptions.isTrivial(); + } + public int numLeadingParams() { return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java index dc769550daf..c2a480ce54d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -24,34 +24,49 @@ */ package jdk.internal.foreign.abi; +import jdk.internal.foreign.Utils; + +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.lang.foreign.ValueLayout.JAVA_INT; +import static sun.security.action.GetPropertyAction.privilegedGetProperty; public enum CapturableState { - GET_LAST_ERROR ("GetLastError", JAVA_INT, 1 << 0), - WSA_GET_LAST_ERROR("WSAGetLastError", JAVA_INT, 1 << 1), - ERRNO ("errno", JAVA_INT, 1 << 2); + GET_LAST_ERROR ("GetLastError", JAVA_INT, 1 << 0, Utils.IS_WINDOWS), + WSA_GET_LAST_ERROR("WSAGetLastError", JAVA_INT, 1 << 1, Utils.IS_WINDOWS), + ERRNO ("errno", JAVA_INT, 1 << 2, true); + + public static final StructLayout LAYOUT = MemoryLayout.structLayout( + supportedStates().map(CapturableState::layout).toArray(MemoryLayout[]::new)); private final String stateName; private final ValueLayout layout; private final int mask; + private final boolean isSupported; - CapturableState(String stateName, ValueLayout layout, int mask) { + CapturableState(String stateName, ValueLayout layout, int mask, boolean isSupported) { this.stateName = stateName; this.layout = layout.withName(stateName); this.mask = mask; + this.isSupported = isSupported; + } + + private static Stream supportedStates() { + return Stream.of(values()).filter(CapturableState::isSupported); } public static CapturableState forName(String name) { return Stream.of(values()) .filter(stl -> stl.stateName().equals(name)) + .filter(CapturableState::isSupported) .findAny() .orElseThrow(() -> new IllegalArgumentException( "Unknown name: " + name +", must be one of: " - + Stream.of(CapturableState.values()) + + supportedStates() .map(CapturableState::stateName) .collect(Collectors.joining(", ")))); } @@ -67,4 +82,8 @@ public enum CapturableState { public int mask() { return mask; } + + public boolean isSupported() { + return isSupported; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java index 8aced592ad4..a6de1e9c09d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java @@ -28,6 +28,7 @@ import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; import sun.security.action.GetPropertyAction; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; import java.lang.invoke.MethodHandle; @@ -50,7 +51,6 @@ public class DowncallLinker { private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); private static final MethodHandle MH_INVOKE_INTERP_BINDINGS; - private static final MethodHandle MH_CHECK_SYMBOL; private static final MethodHandle EMPTY_OBJECT_ARRAY_HANDLE = MethodHandles.constant(Object[].class, new Object[0]); static { @@ -58,8 +58,6 @@ public class DowncallLinker { MethodHandles.Lookup lookup = MethodHandles.lookup(); MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(DowncallLinker.class, "invokeInterpBindings", methodType(Object.class, SegmentAllocator.class, Object[].class, InvocationData.class)); - MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol", - methodType(void.class, MemorySegment.class)); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -86,7 +84,8 @@ public class DowncallLinker { toStorageArray(retMoves), leafType, callingSequence.needsReturnBuffer(), - callingSequence.capturedStateMask() + callingSequence.capturedStateMask(), + callingSequence.needsTransition() ); MethodHandle handle = JLIA.nativeMethodHandle(nep); @@ -111,7 +110,7 @@ public class DowncallLinker { assert handle.type().parameterType(0) == SegmentAllocator.class; assert handle.type().parameterType(1) == MemorySegment.class; - handle = foldArguments(handle, 1, MH_CHECK_SYMBOL); + handle = foldArguments(handle, 1, SharedUtils.MH_CHECK_SYMBOL); handle = SharedUtils.swapArguments(handle, 0, 1); // normalize parameter order @@ -150,10 +149,10 @@ public class DowncallLinker { private record InvocationData(MethodHandle leaf, Map argIndexMap, Map retIndexMap) {} Object invokeInterpBindings(SegmentAllocator allocator, Object[] args, InvocationData invData) throws Throwable { - Binding.Context unboxContext = callingSequence.allocationSize() != 0 - ? Binding.Context.ofBoundedAllocator(callingSequence.allocationSize()) - : Binding.Context.DUMMY; - try (unboxContext) { + Arena unboxArena = callingSequence.allocationSize() != 0 + ? SharedUtils.newBoundedArena(callingSequence.allocationSize()) + : SharedUtils.DUMMY_ARENA; + try (unboxArena) { MemorySegment returnBuffer = null; // do argument processing, get Object[] as result @@ -161,7 +160,7 @@ public class DowncallLinker { if (callingSequence.needsReturnBuffer()) { // we supply the return buffer (argument array does not contain it) Object[] prefixedArgs = new Object[args.length + 1]; - returnBuffer = unboxContext.allocator().allocate(callingSequence.returnBufferSize()); + returnBuffer = unboxArena.allocate(callingSequence.returnBufferSize()); prefixedArgs[0] = returnBuffer; System.arraycopy(args, 0, prefixedArgs, 1, args.length); args = prefixedArgs; @@ -169,7 +168,7 @@ public class DowncallLinker { for (int i = 0; i < args.length; i++) { Object arg = args[i]; BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i), - (storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxContext); + (storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxArena); } // call leaf @@ -190,10 +189,10 @@ public class DowncallLinker { retBufReadOffset += abi.arch.typeSize(storage.type()); return result1; } - }, Binding.Context.ofAllocator(allocator)); + }, allocator); } else { return BindingInterpreter.box(callingSequence.returnBindings(), (storage, type) -> o, - Binding.Context.ofAllocator(allocator)); + allocator); } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index 7b194cf24fd..28cde93090e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -26,13 +26,11 @@ package jdk.internal.foreign.abi; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.StructLayout; -import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.BiConsumer; import java.util.stream.Stream; public class LinkerOptions { @@ -45,14 +43,23 @@ public class LinkerOptions { } public static LinkerOptions forDowncall(FunctionDescriptor desc, Linker.Option... options) { - Map, LinkerOptionImpl> optionMap = new HashMap<>(); + return forShared(LinkerOptionImpl::validateForDowncall, desc, options); + } + + public static LinkerOptions forUpcall(FunctionDescriptor desc, Linker.Option[] options) { + return forShared(LinkerOptionImpl::validateForUpcall, desc, options); + } + + private static LinkerOptions forShared(BiConsumer validator, + FunctionDescriptor desc, Linker.Option... options) { + Map, LinkerOptionImpl> optionMap = new HashMap<>(); for (Linker.Option option : options) { if (optionMap.containsKey(option.getClass())) { throw new IllegalArgumentException("Duplicate option: " + option); } LinkerOptionImpl opImpl = (LinkerOptionImpl) option; - opImpl.validateForDowncall(desc); + validator.accept(opImpl, desc); optionMap.put(option.getClass(), opImpl); } @@ -73,11 +80,11 @@ public class LinkerOptions { } public boolean hasCapturedCallState() { - return getOption(CaptureCallStateImpl.class) != null; + return getOption(CaptureCallState.class) != null; } public Stream capturedCallState() { - CaptureCallStateImpl stl = getOption(CaptureCallStateImpl.class); + CaptureCallState stl = getOption(CaptureCallState.class); return stl == null ? Stream.empty() : stl.saved().stream(); } @@ -86,6 +93,11 @@ public class LinkerOptions { return fva != null; } + public boolean isTrivial() { + IsTrivial it = getOption(IsTrivial.class); + return it != null; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -99,11 +111,14 @@ public class LinkerOptions { } public sealed interface LinkerOptionImpl extends Linker.Option - permits FirstVariadicArg, - CaptureCallStateImpl { + permits CaptureCallState, FirstVariadicArg, IsTrivial { default void validateForDowncall(FunctionDescriptor descriptor) { throw new IllegalArgumentException("Not supported for downcall: " + this); } + + default void validateForUpcall(FunctionDescriptor descriptor) { + throw new IllegalArgumentException("Not supported for upcall: " + this); + } } public record FirstVariadicArg(int index) implements LinkerOptionImpl { @@ -115,22 +130,19 @@ public class LinkerOptions { } } - public record CaptureCallStateImpl(Set saved) implements LinkerOptionImpl, Linker.Option.CaptureCallState { - + public record CaptureCallState(Set saved) implements LinkerOptionImpl { @Override public void validateForDowncall(FunctionDescriptor descriptor) { // done during construction } - - @Override - public StructLayout layout() { - return MemoryLayout.structLayout( - saved.stream() - .sorted(Comparator.comparingInt(CapturableState::ordinal)) - .map(CapturableState::layout) - .toArray(MemoryLayout[]::new) - ); - } } + public record IsTrivial() implements LinkerOptionImpl { + public static IsTrivial INSTANCE = new IsTrivial(); + + @Override + public void validateForDowncall(FunctionDescriptor descriptor) { + // always allowed + } + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java index 9426c404c9f..51c7304b7c6 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -47,7 +47,8 @@ public class NativeEntryPoint { private static final SoftReferenceCache NEP_CACHE = new SoftReferenceCache<>(); private record CacheKey(MethodType methodType, ABIDescriptor abi, List argMoves, List retMoves, - boolean needsReturnBuffer, int capturedStateMask) {} + boolean needsReturnBuffer, int capturedStateMask, + boolean needsTransition) {} private NativeEntryPoint(MethodType methodType, long downcallStubAddress) { this.methodType = methodType; @@ -58,15 +59,18 @@ public class NativeEntryPoint { VMStorage[] argMoves, VMStorage[] returnMoves, MethodType methodType, boolean needsReturnBuffer, - int capturedStateMask) { + int capturedStateMask, + boolean needsTransition) { if (returnMoves.length > 1 != needsReturnBuffer) { throw new AssertionError("Multiple register return, but needsReturnBuffer was false"); } checkType(methodType, needsReturnBuffer, capturedStateMask); - CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), needsReturnBuffer, capturedStateMask); + CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), + needsReturnBuffer, capturedStateMask, needsTransition); return NEP_CACHE.get(key, k -> { - long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer, capturedStateMask); + long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer, + capturedStateMask, needsTransition); NativeEntryPoint nep = new NativeEntryPoint(methodType, downcallStub); CLEANER.register(nep, () -> freeDowncallStub(downcallStub)); return nep; @@ -87,7 +91,8 @@ public class NativeEntryPoint { private static native long makeDowncallStub(MethodType methodType, ABIDescriptor abi, VMStorage[] encArgMoves, VMStorage[] encRetMoves, boolean needsReturnBuffer, - int capturedStateMask); + int capturedStateMask, + boolean needsTransition); private static native boolean freeDowncallStub0(long downcallStub); private static void freeDowncallStub(long downcallStub) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java index 26a1ee2262c..fc800e073ad 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java @@ -32,19 +32,21 @@ import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker; +import jdk.internal.foreign.abi.fallback.FallbackLinker; import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; import jdk.internal.vm.annotation.ForceInline; +import java.lang.foreign.AddressLayout; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.MemorySegment.Scope; import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.VaList; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -55,9 +57,7 @@ import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -76,6 +76,28 @@ public final class SharedUtils { private static final MethodHandle MH_ALLOC_BUFFER; private static final MethodHandle MH_BUFFER_COPY; private static final MethodHandle MH_REACHABILITY_FENCE; + public static final MethodHandle MH_CHECK_SYMBOL; + + public static final AddressLayout C_POINTER = ADDRESS + .withBitAlignment(64) + .withTargetLayout(MemoryLayout.sequenceLayout(JAVA_BYTE)); + + public static final Arena DUMMY_ARENA = new Arena() { + @Override + public Scope scope() { + throw new UnsupportedOperationException(); + } + + @Override + public MemorySegment allocate(long byteSize) { + throw new UnsupportedOperationException(); + } + + @Override + public void close() { + // do nothing + } + }; static { try { @@ -86,6 +108,8 @@ public final class SharedUtils { methodType(MemorySegment.class, MemorySegment.class, MemorySegment.class)); MH_REACHABILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence", methodType(void.class, Object.class)); + MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol", + methodType(void.class, MemorySegment.class)); } catch (ReflectiveOperationException e) { throw new BootstrapMethodError(e); } @@ -214,6 +238,8 @@ public final class SharedUtils { case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance(); case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance(); case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance(); + case FALLBACK -> FallbackLinker.getInstance(); + case UNSUPPORTED -> throw new UnsupportedOperationException("Platform does not support native linker"); }; } @@ -265,7 +291,7 @@ public final class SharedUtils { } - static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) { + public static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) { MethodType mtype = mh.type(); int[] perms = new int[mtype.parameterCount()]; MethodType swappedType = MethodType.methodType(mtype.returnType()); @@ -283,10 +309,14 @@ public final class SharedUtils { return MH_REACHABILITY_FENCE.asType(MethodType.methodType(void.class, type)); } - static void handleUncaughtException(Throwable t) { + public static void handleUncaughtException(Throwable t) { if (t != null) { - t.printStackTrace(); - JLA.exit(1); + try { + t.printStackTrace(); + System.err.println("Unrecoverable uncaught exception encountered. The VM will now exit"); + } finally { + JLA.exit(1); + } } } @@ -319,39 +349,6 @@ public final class SharedUtils { throw new IllegalArgumentException("Symbol is NULL: " + symbol); } - public static VaList newVaList(Consumer actions, SegmentScope scope) { - return switch (CABI.current()) { - case WIN_64 -> Windowsx64Linker.newVaList(actions, scope); - case SYS_V -> SysVx64Linker.newVaList(actions, scope); - case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope); - case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope); - case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaList(actions, scope); - case WIN_AARCH_64 -> WindowsAArch64Linker.newVaList(actions, scope); - }; - } - - public static VaList newVaListOfAddress(long address, SegmentScope scope) { - return switch (CABI.current()) { - case WIN_64 -> Windowsx64Linker.newVaListOfAddress(address, scope); - case SYS_V -> SysVx64Linker.newVaListOfAddress(address, scope); - case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, scope); - case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope); - case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaListOfAddress(address, scope); - case WIN_AARCH_64 -> WindowsAArch64Linker.newVaListOfAddress(address, scope); - }; - } - - public static VaList emptyVaList() { - return switch (CABI.current()) { - case WIN_64 -> Windowsx64Linker.emptyVaList(); - case SYS_V -> SysVx64Linker.emptyVaList(); - case LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList(); - case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList(); - case LINUX_RISCV_64 -> LinuxRISCV64Linker.emptyVaList(); - case WIN_AARCH_64 -> WindowsAArch64Linker.emptyVaList(); - }; - } - static void checkType(Class actualType, Class expectedType) { if (expectedType != actualType) { throw new IllegalArgumentException( @@ -369,8 +366,47 @@ public final class SharedUtils { : chunkOffset; } - public static NoSuchElementException newVaListNSEE(MemoryLayout layout) { - return new NoSuchElementException("No such element: " + layout); + public static Arena newBoundedArena(long size) { + return new Arena() { + final Arena arena = Arena.ofConfined(); + final SegmentAllocator slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size)); + + @Override + public Scope scope() { + return arena.scope(); + } + + @Override + public void close() { + arena.close(); + } + + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + return slicingAllocator.allocate(byteSize, byteAlignment); + } + }; + } + + public static Arena newEmptyArena() { + return new Arena() { + final Arena arena = Arena.ofConfined(); + + @Override + public Scope scope() { + return arena.scope(); + } + + @Override + public void close() { + arena.close(); + } + + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + throw new UnsupportedOperationException(); + } + }; } public static final class SimpleVaArg { @@ -387,59 +423,6 @@ public final class SharedUtils { } } - public static final class EmptyVaList implements VaList { - - private final MemorySegment address; - - public EmptyVaList(MemorySegment address) { - this.address = address; - } - - private static UnsupportedOperationException uoe() { - return new UnsupportedOperationException("Empty VaList"); - } - - @Override - public int nextVarg(ValueLayout.OfInt layout) { - throw uoe(); - } - - @Override - public long nextVarg(ValueLayout.OfLong layout) { - throw uoe(); - } - - @Override - public double nextVarg(ValueLayout.OfDouble layout) { - throw uoe(); - } - - @Override - public MemorySegment nextVarg(ValueLayout.OfAddress layout) { - throw uoe(); - } - - @Override - public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { - throw uoe(); - } - - @Override - public void skip(MemoryLayout... layouts) { - throw uoe(); - } - - @Override - public VaList copy() { - return this; - } - - @Override - public MemorySegment segment() { - return address; - } - } - static void writeOverSized(MemorySegment ptr, Class type, Object o) { // use VH_LONG for integers to zero out the whole register in the process if (type == long.class) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java index ecc14b5ff33..7a3229ad531 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java @@ -28,8 +28,8 @@ package jdk.internal.foreign.abi; import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory; import sun.security.action.GetPropertyAction; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -141,9 +141,9 @@ public class UpcallLinker { ABIDescriptor abi) {} private static Object invokeInterpBindings(MethodHandle leaf, Object[] lowLevelArgs, InvocationData invData) throws Throwable { - Binding.Context allocator = invData.callingSequence.allocationSize() != 0 - ? Binding.Context.ofBoundedAllocator(invData.callingSequence.allocationSize()) - : Binding.Context.ofScope(); + Arena allocator = invData.callingSequence.allocationSize() != 0 + ? SharedUtils.newBoundedArena(invData.callingSequence.allocationSize()) + : SharedUtils.newEmptyArena(); try (allocator) { /// Invoke interpreter, got array of high-level arguments back Object[] highLevelArgs = new Object[invData.callingSequence.calleeMethodType().parameterCount()]; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java index 50f6a14d8d0..854e5643dc9 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,7 +25,7 @@ package jdk.internal.foreign.abi; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import jdk.internal.foreign.MemorySessionImpl; @@ -50,13 +50,13 @@ public final class UpcallStubs { registerNatives(); } - static MemorySegment makeUpcall(long entry, SegmentScope scope) { - ((MemorySessionImpl) scope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { + static MemorySegment makeUpcall(long entry, Arena arena) { + MemorySessionImpl.toMemorySession(arena).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { @Override public void cleanup() { freeUpcallStub(entry); } }); - return MemorySegment.ofAddress(entry, 0, scope); + return MemorySegment.ofAddress(entry).reinterpret(arena, null); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/AArch64Architecture.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/AArch64Architecture.java index 38e18d17819..bfd8dd191d9 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/AArch64Architecture.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/AArch64Architecture.java @@ -30,7 +30,7 @@ import jdk.internal.foreign.abi.Architecture; import jdk.internal.foreign.abi.StubLocations; import jdk.internal.foreign.abi.VMStorage; -public class AArch64Architecture implements Architecture { +public final class AArch64Architecture implements Architecture { public static final Architecture INSTANCE = new AArch64Architecture(); private static final short REG64_MASK = 0b0000_0000_0000_0001; @@ -39,6 +39,9 @@ public class AArch64Architecture implements Architecture { private static final int INTEGER_REG_SIZE = 8; private static final int VECTOR_REG_SIZE = 16; + // Suppresses default constructor, ensuring non-instantiability. + private AArch64Architecture() {} + @Override public boolean isStackType(int cls) { return cls == StorageType.STACK; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java index 44be50175d7..afcd73aaf5f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java @@ -25,6 +25,7 @@ */ package jdk.internal.foreign.abi.aarch64; +import java.lang.foreign.AddressLayout; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; @@ -44,14 +45,12 @@ import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger; import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64CallArranger; import jdk.internal.foreign.Utils; -import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.List; import java.util.Optional; -import static jdk.internal.foreign.PlatformLayouts.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*; @@ -82,7 +81,7 @@ public abstract class CallArranger { // // Although the AAPCS64 says r0-7 and v0-7 are all valid return // registers, it's not possible to generate a C function that uses - // r2-7 and v4-7 so they are omitted here. + // r2-7 and v4-7 so, they are omitted here. protected static final ABIDescriptor C = abiFor( new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT}, new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 }, @@ -158,7 +157,7 @@ public abstract class CallArranger { boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); if (returnInMemory) { - csb.addArgumentBindings(MemorySegment.class, AArch64.C_POINTER, + csb.addArgumentBindings(MemorySegment.class, SharedUtils.C_POINTER, argCalc.getIndirectBindings()); } else if (cDesc.returnLayout().isPresent()) { Class carrier = mt.returnType(); @@ -190,8 +189,9 @@ public abstract class CallArranger { return handle; } - public UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) { - Bindings bindings = getBindings(mt, cDesc, true); + public UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, + LinkerOptions options) { + Bindings bindings = getBindings(mt, cDesc, true, options); final boolean dropReturn = true; /* drop return, since we don't have bindings for it */ return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, abiDescriptor(), bindings.callingSequence); @@ -429,7 +429,7 @@ public abstract class CallArranger { assert carrier == MemorySegment.class; bindings.copy(layout) .unboxAddress(); - VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, AArch64.C_POINTER); + VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, SharedUtils.C_POINTER); bindings.vmStore(storage, long.class); } case POINTER -> { @@ -464,7 +464,7 @@ public abstract class CallArranger { List getIndirectBindings() { return Binding.builder() .vmLoad(INDIRECT_RESULT, long.class) - .boxAddressRaw(Long.MAX_VALUE) + .boxAddressRaw(Long.MAX_VALUE, 1) .build(); } @@ -480,22 +480,25 @@ public abstract class CallArranger { StorageCalculator.StructStorage[] structStorages = storageCalculator.structStorages((GroupLayout) layout, forHFA); - for (StorageCalculator.StructStorage structStorage : structStorages) { + for (StorageCalculator.StructStorage( + long offset, Class ca, int byteWidth, VMStorage storage + ) : structStorages) { bindings.dup(); - bindings.vmLoad(structStorage.storage(), structStorage.carrier()) - .bufferStore(structStorage.offset(), structStorage.carrier(), structStorage.byteWidth()); + bindings.vmLoad(storage, ca) + .bufferStore(offset, ca, byteWidth); } } case STRUCT_REFERENCE -> { assert carrier == MemorySegment.class; - VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, AArch64.C_POINTER); + VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, SharedUtils.C_POINTER); bindings.vmLoad(storage, long.class) .boxAddress(layout); } case POINTER -> { - VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, (ValueLayout) layout); + AddressLayout addressLayout = (AddressLayout) layout; + VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, addressLayout); bindings.vmLoad(storage, long.class) - .boxAddressRaw(Utils.pointeeSize(layout)); + .boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout)); } case INTEGER -> { VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, (ValueLayout) layout); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java index 4b1cf064202..6c7fed1a481 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java @@ -29,12 +29,9 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.aarch64.CallArranger; -import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.util.function.Consumer; /** * ABI implementation based on ARM document "Procedure Call Standard for @@ -60,21 +57,7 @@ public final class LinuxAArch64Linker extends AbstractLinker { } @Override - protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) { - return CallArranger.LINUX.arrangeUpcall(targetType, function); - } - - public static VaList newVaList(Consumer actions, SegmentScope scope) { - LinuxAArch64VaList.Builder builder = LinuxAArch64VaList.builder(scope); - actions.accept(builder); - return builder.build(); - } - - public static VaList newVaListOfAddress(long address, SegmentScope scope) { - return LinuxAArch64VaList.ofAddress(address, scope); - } - - public static VaList emptyVaList() { - return LinuxAArch64VaList.empty(); + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.LINUX.arrangeUpcall(targetType, function, options); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java deleted file mode 100644 index e165016a27b..00000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, 2021, Arm Limited. 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.foreign.abi.aarch64.linux; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.ValueLayout; -import java.lang.foreign.VaList; -import java.lang.foreign.SegmentAllocator; -import jdk.internal.foreign.abi.aarch64.TypeClass; -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.Utils; -import jdk.internal.foreign.abi.SharedUtils; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static jdk.internal.foreign.PlatformLayouts.AArch64; - -import static java.lang.foreign.MemoryLayout.PathElement.groupElement; -import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; -import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; -import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMENTS; - -/** - * Standard va_list implementation as defined by AAPCS document and used on - * Linux. Variadic parameters may be passed in registers or on the stack. - */ -public non-sealed class LinuxAArch64VaList implements VaList { - - // See AAPCS Appendix B "Variable Argument Lists" for definition of - // va_list on AArch64. - // - // typedef struct __va_list { - // void *__stack; // next stack param - // void *__gr_top; // end of GP arg reg save area - // void *__vr_top; // end of FP/SIMD arg reg save area - // int __gr_offs; // offset from __gr_top to next GP register arg - // int __vr_offs; // offset from __vr_top to next FP/SIMD register arg - // } va_list; - - static final GroupLayout LAYOUT = MemoryLayout.structLayout( - AArch64.C_POINTER.withName("__stack"), - AArch64.C_POINTER.withName("__gr_top"), - AArch64.C_POINTER.withName("__vr_top"), - AArch64.C_INT.withName("__gr_offs"), - AArch64.C_INT.withName("__vr_offs") - ).withName("__va_list"); - - private static final long STACK_SLOT_SIZE = 8; - - private static final MemoryLayout GP_REG - = MemoryLayout.paddingLayout(64).withBitAlignment(64); - private static final MemoryLayout FP_REG - = MemoryLayout.paddingLayout(128).withBitAlignment(128); - - private static final MemoryLayout LAYOUT_GP_REGS - = MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, GP_REG); - private static final MemoryLayout LAYOUT_FP_REGS - = MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, FP_REG); - - private static final int GP_SLOT_SIZE = (int) GP_REG.byteSize(); - private static final int FP_SLOT_SIZE = (int) FP_REG.byteSize(); - - private static final int MAX_GP_OFFSET = (int) LAYOUT_GP_REGS.byteSize(); - private static final int MAX_FP_OFFSET = (int) LAYOUT_FP_REGS.byteSize(); - - private static final VarHandle VH_stack = LAYOUT.varHandle(groupElement("__stack")); - private static final VarHandle VH_gr_top = LAYOUT.varHandle(groupElement("__gr_top")); - private static final VarHandle VH_vr_top = LAYOUT.varHandle(groupElement("__vr_top")); - private static final VarHandle VH_gr_offs - = LAYOUT.varHandle(groupElement("__gr_offs")); - private static final VarHandle VH_vr_offs - = LAYOUT.varHandle(groupElement("__vr_offs")); - - private static final VaList EMPTY - = new SharedUtils.EmptyVaList(emptyListAddress()); - - private final MemorySegment segment; - private MemorySegment stack; - private final MemorySegment gpRegsArea; - private final long gpLimit; - private final MemorySegment fpRegsArea; - private final long fpLimit; - - private LinuxAArch64VaList(MemorySegment segment, MemorySegment stack, - MemorySegment gpRegsArea, long gpLimit, MemorySegment fpRegsArea, long fpLimit) { - this.segment = segment; - this.stack = stack; - this.gpRegsArea = gpRegsArea; - this.gpLimit = gpLimit; - this.fpRegsArea = fpRegsArea; - this.fpLimit = fpLimit; - } - - private static LinuxAArch64VaList readFromAddress(long address, SegmentScope scope) { - MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope); - MemorySegment stack = stackPtr(segment); // size unknown - MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).address() - MAX_GP_OFFSET, MAX_GP_OFFSET, scope); - MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).address() - MAX_FP_OFFSET, MAX_FP_OFFSET, scope); - return new LinuxAArch64VaList(segment, stack, gpRegsArea, MAX_GP_OFFSET, fpRegsArea, MAX_FP_OFFSET); - } - - private static MemorySegment emptyListAddress() { - MemorySegment ms = MemorySegment.allocateNative(LAYOUT, SegmentScope.global()); - VH_stack.set(ms, MemorySegment.NULL); - VH_gr_top.set(ms, MemorySegment.NULL); - VH_vr_top.set(ms, MemorySegment.NULL); - VH_gr_offs.set(ms, 0); - VH_vr_offs.set(ms, 0); - return ms.asSlice(0, 0); - } - - public static VaList empty() { - return EMPTY; - } - - private MemorySegment grTop() { - return grTop(segment); - } - - private static MemorySegment grTop(MemorySegment segment) { - return (MemorySegment) VH_gr_top.get(segment); - } - - private MemorySegment vrTop() { - return vrTop(segment); - } - - private static MemorySegment vrTop(MemorySegment segment) { - return (MemorySegment) VH_vr_top.get(segment); - } - - private int grOffs() { - final int offs = (int) VH_gr_offs.get(segment); - assert offs <= 0; - return offs; - } - - private int vrOffs() { - final int offs = (int) VH_vr_offs.get(segment); - assert offs <= 0; - return offs; - } - - private static MemorySegment stackPtr(MemorySegment segment) { - return (MemorySegment) VH_stack.get(segment); - } - - private MemorySegment stackPtr() { - return stackPtr(segment); - } - - private void setStack(MemorySegment newStack) { - stack = newStack; - VH_stack.set(segment, stack); - } - - private void consumeGPSlots(int num) { - final int old = (int) VH_gr_offs.get(segment); - VH_gr_offs.set(segment, old + num * GP_SLOT_SIZE); - } - - private void consumeFPSlots(int num) { - final int old = (int) VH_vr_offs.get(segment); - VH_vr_offs.set(segment, old + num * FP_SLOT_SIZE); - } - - private long currentGPOffset() { - // Offset from start of GP register segment. __gr_top points to the top - // (highest address) of the GP registers area. __gr_offs is the negative - // offset of next saved register from the top. - - return gpRegsArea.byteSize() + grOffs(); - } - - private long currentFPOffset() { - // Offset from start of FP register segment. __vr_top points to the top - // (highest address) of the FP registers area. __vr_offs is the negative - // offset of next saved register from the top. - - return fpRegsArea.byteSize() + vrOffs(); - } - - private long preAlignOffset(MemoryLayout layout) { - long alignmentOffset = 0; - if (layout.byteAlignment() > STACK_SLOT_SIZE) { - long addr = stack.address(); - alignmentOffset = Utils.alignUp(addr, 16) - addr; - } - return alignmentOffset; - } - - private void preAlignStack(MemoryLayout layout) { - setStack(stack.asSlice(preAlignOffset(layout))); - } - - private void postAlignStack(MemoryLayout layout) { - setStack(stack.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE))); - } - - @Override - public int nextVarg(ValueLayout.OfInt layout) { - return (int) read(layout); - } - - @Override - public long nextVarg(ValueLayout.OfLong layout) { - return (long) read(layout); - } - - @Override - public double nextVarg(ValueLayout.OfDouble layout) { - return (double) read(layout); - } - - @Override - public MemorySegment nextVarg(ValueLayout.OfAddress layout) { - return (MemorySegment) read(layout); - } - - @Override - public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(allocator); - return (MemorySegment) read( layout, allocator); - } - - private Object read(MemoryLayout layout) { - return read(layout, THROWING_ALLOCATOR); - } - - private Object read(MemoryLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(layout); - TypeClass typeClass = TypeClass.classifyLayout(layout); - if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) { - checkStackElement(layout); - preAlignStack(layout); - return switch (typeClass) { - case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> { - MemorySegment slice = stack.asSlice(0, layout.byteSize()); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(slice); - postAlignStack(layout); - yield seg; - } - case POINTER, INTEGER, FLOAT -> { - VarHandle reader = layout.varHandle(); - MemorySegment slice = stack.asSlice(0, layout.byteSize()); - Object res = reader.get(slice); - postAlignStack(layout); - yield res; - } - }; - } else { - return switch (typeClass) { - case STRUCT_REGISTER -> { - checkGPElement(layout, numSlots(layout)); - // Struct is passed packed in integer registers. - MemorySegment value = allocator.allocate(layout); - long offset = 0; - while (offset < layout.byteSize()) { - final long copy = Math.min(layout.byteSize() - offset, 8); - MemorySegment.copy(gpRegsArea, currentGPOffset(), value, offset, copy); - consumeGPSlots(1); - offset += copy; - } - yield value; - } - case STRUCT_HFA -> { - checkFPElement(layout, numSlots(layout)); - // Struct is passed with each element in a separate floating - // point register. - MemorySegment value = allocator.allocate(layout); - GroupLayout group = (GroupLayout)layout; - long offset = 0; - for (MemoryLayout elem : group.memberLayouts()) { - assert elem.byteSize() <= 8; - final long copy = elem.byteSize(); - MemorySegment.copy(fpRegsArea, currentFPOffset(), value, offset, copy); - consumeFPSlots(1); - offset += copy; - } - yield value; - } - case STRUCT_REFERENCE -> { - checkGPElement(layout, 1); - // Struct is passed indirectly via a pointer in an integer register. - VarHandle ptrReader = AArch64.C_POINTER.varHandle(); - MemorySegment ptr = (MemorySegment) ptrReader.get( - gpRegsArea.asSlice(currentGPOffset())); - consumeGPSlots(1); - - MemorySegment slice = MemorySegment.ofAddress(ptr.address(), layout.byteSize(), segment.scope()); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(slice); - yield seg; - } - case POINTER, INTEGER -> { - checkGPElement(layout, 1); - VarHandle reader = layout.varHandle(); - Object res = reader.get(gpRegsArea.asSlice(currentGPOffset())); - consumeGPSlots(1); - yield res; - } - case FLOAT -> { - checkFPElement(layout, 1); - VarHandle reader = layout.varHandle(); - Object res = reader.get(fpRegsArea.asSlice(currentFPOffset())); - consumeFPSlots(1); - yield res; - } - }; - } - } - - private void checkGPElement(MemoryLayout layout, long slots) { - if ((grOffs() + MAX_GP_OFFSET) + (slots * GP_SLOT_SIZE) > gpLimit) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - private void checkFPElement(MemoryLayout layout, long slots) { - if ((vrOffs() + MAX_FP_OFFSET) + (slots * FP_SLOT_SIZE) > fpLimit) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - private void checkStackElement(MemoryLayout layout) { - if (preAlignOffset(layout) + layout.byteSize() > stack.byteSize()) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - @Override - public void skip(MemoryLayout... layouts) { - Objects.requireNonNull(layouts); - ((MemorySessionImpl) segment.scope()).checkValidState(); - for (MemoryLayout layout : layouts) { - Objects.requireNonNull(layout); - TypeClass typeClass = TypeClass.classifyLayout(layout); - if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) { - checkStackElement(layout); - preAlignStack(layout); - postAlignStack(layout); - } else if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) { - long slots = numSlots(layout); - checkFPElement(layout, slots); - consumeFPSlots((int) slots); - } else if (typeClass == TypeClass.STRUCT_REFERENCE) { - checkGPElement(layout, 1); - consumeGPSlots(1); - } else { - long slots = numSlots(layout); - checkGPElement(layout, slots); - consumeGPSlots((int) slots); - } - } - } - - static LinuxAArch64VaList.Builder builder(SegmentScope scope) { - return new LinuxAArch64VaList.Builder(scope); - } - - public static VaList ofAddress(long address, SegmentScope scope) { - return readFromAddress(address, scope); - } - - @Override - public VaList copy() { - MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope()); - copy.copyFrom(segment); - return new LinuxAArch64VaList(copy, stack, gpRegsArea, gpLimit, fpRegsArea, fpLimit); - } - - @Override - public MemorySegment segment() { - // make sure that returned segment cannot be accessed - return segment.asSlice(0, 0); - } - - private static long numSlots(MemoryLayout layout) { - return Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) / STACK_SLOT_SIZE; - } - - private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset, - TypeClass typeClass, MemoryLayout layout) { - if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) { - return currentFPOffset > MAX_FP_OFFSET - numSlots(layout) * FP_SLOT_SIZE; - } else if (typeClass == TypeClass.STRUCT_REFERENCE) { - return currentGPOffset > MAX_GP_OFFSET - GP_SLOT_SIZE; - } else { - return currentGPOffset > MAX_GP_OFFSET - numSlots(layout) * GP_SLOT_SIZE; - } - } - - @Override - public String toString() { - return "LinuxAArch64VaList{" - + "__stack=" + stackPtr() - + ", __gr_top=" + grTop() - + ", __vr_top=" + vrTop() - + ", __gr_offs=" + grOffs() - + ", __vr_offs=" + vrOffs() - + '}'; - } - - public static non-sealed class Builder implements VaList.Builder { - private final SegmentScope scope; - private final MemorySegment gpRegs; - private final MemorySegment fpRegs; - - private long currentGPOffset = 0; - private long currentFPOffset = 0; - private final List stackArgs = new ArrayList<>(); - - Builder(SegmentScope scope) { - this.scope = scope; - this.gpRegs = MemorySegment.allocateNative(LAYOUT_GP_REGS, scope); - this.fpRegs = MemorySegment.allocateNative(LAYOUT_FP_REGS, scope); - } - - @Override - public Builder addVarg(ValueLayout.OfInt layout, int value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfLong layout, long value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfDouble layout, double value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(GroupLayout layout, MemorySegment value) { - return arg(layout, value); - } - - private Builder arg(MemoryLayout layout, Object value) { - Objects.requireNonNull(layout); - Objects.requireNonNull(value); - TypeClass typeClass = TypeClass.classifyLayout(layout); - if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) { - stackArgs.add(new SimpleVaArg(layout, value)); - } else { - switch (typeClass) { - case STRUCT_REGISTER -> { - // Struct is passed packed in integer registers. - MemorySegment valueSegment = (MemorySegment) value; - long offset = 0; - while (offset < layout.byteSize()) { - final long copy = Math.min(layout.byteSize() - offset, 8); - MemorySegment.copy(valueSegment, offset, gpRegs, currentGPOffset, copy); - currentGPOffset += GP_SLOT_SIZE; - offset += copy; - } - } - case STRUCT_HFA -> { - // Struct is passed with each element in a separate floating - // point register. - MemorySegment valueSegment = (MemorySegment) value; - GroupLayout group = (GroupLayout)layout; - long offset = 0; - for (MemoryLayout elem : group.memberLayouts()) { - assert elem.byteSize() <= 8; - final long copy = elem.byteSize(); - MemorySegment.copy(valueSegment, offset, fpRegs, currentFPOffset, copy); - currentFPOffset += FP_SLOT_SIZE; - offset += copy; - } - } - case STRUCT_REFERENCE -> { - // Struct is passed indirectly via a pointer in an integer register. - MemorySegment valueSegment = (MemorySegment) value; - VarHandle writer = AArch64.C_POINTER.varHandle(); - writer.set(gpRegs.asSlice(currentGPOffset), - valueSegment); - currentGPOffset += GP_SLOT_SIZE; - } - case POINTER, INTEGER -> { - VarHandle writer = layout.varHandle(); - writer.set(gpRegs.asSlice(currentGPOffset), value); - currentGPOffset += GP_SLOT_SIZE; - } - case FLOAT -> { - VarHandle writer = layout.varHandle(); - writer.set(fpRegs.asSlice(currentFPOffset), value); - currentFPOffset += FP_SLOT_SIZE; - } - } - } - return this; - } - - private boolean isEmpty() { - return currentGPOffset == 0 && currentFPOffset == 0 && stackArgs.isEmpty(); - } - - public VaList build() { - if (isEmpty()) { - return EMPTY; - } - - MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, scope); - MemorySegment stackArgsSegment; - if (!stackArgs.isEmpty()) { - long stackArgsSize = stackArgs.stream() - .reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum); - stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope); - MemorySegment writeCursor = stackArgsSegment; - for (SimpleVaArg arg : stackArgs) { - final long alignedSize = Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE); - writeCursor = Utils.alignUp(writeCursor, alignedSize); - VarHandle writer = arg.varHandle(); - writer.set(writeCursor, arg.value); - writeCursor = writeCursor.asSlice(alignedSize); - } - } else { - stackArgsSegment = MemorySegment.NULL; - } - - VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize())); - VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.byteSize())); - VH_stack.set(vaListSegment, stackArgsSegment); - VH_gr_offs.set(vaListSegment, -MAX_GP_OFFSET); - VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET); - - assert MemorySessionImpl.sameOwnerThread(gpRegs.scope(), vaListSegment.scope()); - assert MemorySessionImpl.sameOwnerThread(fpRegs.scope(), vaListSegment.scope()); - return new LinuxAArch64VaList(vaListSegment, stackArgsSegment, gpRegs, currentGPOffset, fpRegs, currentFPOffset); - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java index 05264c4505c..3e18c43d2ba 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java @@ -30,11 +30,8 @@ import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.util.function.Consumer; /** * ABI implementation for macOS on Apple silicon. Based on AAPCS with @@ -60,21 +57,7 @@ public final class MacOsAArch64Linker extends AbstractLinker { } @Override - protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) { - return CallArranger.MACOS.arrangeUpcall(targetType, function); - } - - public static VaList newVaList(Consumer actions, SegmentScope scope) { - MacOsAArch64VaList.Builder builder = MacOsAArch64VaList.builder(scope); - actions.accept(builder); - return builder.build(); - } - - public static VaList newVaListOfAddress(long address, SegmentScope scope) { - return MacOsAArch64VaList.ofAddress(address, scope); - } - - public static VaList emptyVaList() { - return MacOsAArch64VaList.empty(); + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.MACOS.arrangeUpcall(targetType, function, options); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java deleted file mode 100644 index 210b66ecde3..00000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, Arm Limited. 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.foreign.abi.aarch64.macos; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.VaList; -import java.lang.foreign.ValueLayout; -import jdk.internal.foreign.abi.aarch64.TypeClass; -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER; -import static jdk.internal.foreign.abi.SharedUtils.alignUp; - -/** - * Simplified va_list implementation used on macOS where all variadic - * parameters are passed on the stack and the type of va_list decays to - * char* instead of the structure defined in the AAPCS. - */ -public non-sealed class MacOsAArch64VaList implements VaList { - private static final long VA_SLOT_SIZE_BYTES = 8; - private static final VarHandle VH_address = C_POINTER.varHandle(); - - private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL); - - private MemorySegment segment; - - private MacOsAArch64VaList(MemorySegment segment) { - this.segment = segment; - } - - public static VaList empty() { - return EMPTY; - } - - @Override - public int nextVarg(ValueLayout.OfInt layout) { - return (int) read(layout); - } - - @Override - public long nextVarg(ValueLayout.OfLong layout) { - return (long) read(layout); - } - - @Override - public double nextVarg(ValueLayout.OfDouble layout) { - return (double) read(layout); - } - - @Override - public MemorySegment nextVarg(ValueLayout.OfAddress layout) { - return (MemorySegment) read(layout); - } - - @Override - public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(allocator); - return (MemorySegment) read(layout, allocator); - } - - private Object read(MemoryLayout layout) { - return read(layout, SharedUtils.THROWING_ALLOCATOR); - } - - private Object read(MemoryLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(layout); - Object res; - if (layout instanceof GroupLayout) { - TypeClass typeClass = TypeClass.classifyLayout(layout); - res = switch (typeClass) { - case STRUCT_REFERENCE -> { - checkElement(layout, VA_SLOT_SIZE_BYTES); - MemorySegment structAddr = (MemorySegment) VH_address.get(segment); - MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope()); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(struct); - segment = segment.asSlice(VA_SLOT_SIZE_BYTES); - yield seg; - } - case STRUCT_REGISTER, STRUCT_HFA -> { - long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES); - checkElement(layout, size); - MemorySegment struct = allocator.allocate(layout) - .copyFrom(segment.asSlice(0, layout.byteSize())); - segment = segment.asSlice(size); - yield struct; - } - default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); - }; - } else { - checkElement(layout, VA_SLOT_SIZE_BYTES); - VarHandle reader = layout.varHandle(); - res = reader.get(segment); - segment = segment.asSlice(VA_SLOT_SIZE_BYTES); - } - return res; - } - - private static long sizeOf(MemoryLayout layout) { - return switch (TypeClass.classifyLayout(layout)) { - case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES); - default -> VA_SLOT_SIZE_BYTES; - }; - } - - @Override - public void skip(MemoryLayout... layouts) { - Objects.requireNonNull(layouts); - ((MemorySessionImpl) segment.scope()).checkValidState(); - - for (MemoryLayout layout : layouts) { - Objects.requireNonNull(layout); - long size = sizeOf(layout); - checkElement(layout, size); - segment = segment.asSlice(size); - } - } - - private void checkElement(MemoryLayout layout, long size) { - if (segment.byteSize() < size) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - static MacOsAArch64VaList ofAddress(long address, SegmentScope session) { - MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session); - return new MacOsAArch64VaList(segment); - } - - static Builder builder(SegmentScope session) { - return new Builder(session); - } - - @Override - public VaList copy() { - ((MemorySessionImpl) segment.scope()).checkValidState(); - return new MacOsAArch64VaList(segment); - } - - @Override - public MemorySegment segment() { - // make sure that returned segment cannot be accessed - return segment.asSlice(0, 0); - } - - public static non-sealed class Builder implements VaList.Builder { - - private final SegmentScope session; - private final List args = new ArrayList<>(); - - public Builder(SegmentScope session) { - ((MemorySessionImpl) session).checkValidState(); - this.session = session; - } - - private Builder arg(MemoryLayout layout, Object value) { - Objects.requireNonNull(layout); - Objects.requireNonNull(value); - args.add(new SimpleVaArg(layout, value)); - return this; - } - - @Override - public Builder addVarg(ValueLayout.OfInt layout, int value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfLong layout, long value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfDouble layout, double value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(GroupLayout layout, MemorySegment value) { - return arg(layout, value); - } - - public VaList build() { - if (args.isEmpty()) { - return EMPTY; - } - - long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum); - MemorySegment segment = MemorySegment.allocateNative(allocationSize, session); - MemorySegment cursor = segment; - - for (SimpleVaArg arg : args) { - if (arg.layout instanceof GroupLayout) { - MemorySegment msArg = ((MemorySegment) arg.value); - TypeClass typeClass = TypeClass.classifyLayout(arg.layout); - switch (typeClass) { - case STRUCT_REFERENCE -> { - MemorySegment copy = MemorySegment.allocateNative(arg.layout, session); - copy.copyFrom(msArg); // by-value - VH_address.set(cursor, copy); - cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); - } - case STRUCT_REGISTER, STRUCT_HFA -> - cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize())) - .asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES)); - default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); - } - } else { - VarHandle writer = arg.varHandle(); - writer.set(cursor, arg.value); - cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); - } - } - - return new MacOsAArch64VaList(segment); - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java index 4fd2da93bdd..5f5478812f2 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java @@ -31,11 +31,8 @@ import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.util.function.Consumer; /** * ABI implementation for Windows/AArch64. Based on AAPCS with @@ -57,22 +54,7 @@ public final class WindowsAArch64Linker extends AbstractLinker { } @Override - protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) { - return CallArranger.WINDOWS.arrangeUpcall(targetType, function); + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.WINDOWS.arrangeUpcall(targetType, function, options); } - - public static VaList newVaList(Consumer actions, SegmentScope scope) { - WindowsAArch64VaList.Builder builder = WindowsAArch64VaList.builder(scope); - actions.accept(builder); - return builder.build(); - } - - public static VaList newVaListOfAddress(long address, SegmentScope scope) { - return WindowsAArch64VaList.ofAddress(address, scope); - } - - public static VaList emptyVaList() { - return WindowsAArch64VaList.empty(); - } - } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64VaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64VaList.java deleted file mode 100644 index e6ca31537d9..00000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64VaList.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, Arm Limited. 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.foreign.abi.aarch64.windows; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.VaList; -import java.lang.foreign.ValueLayout; -import jdk.internal.foreign.abi.aarch64.TypeClass; -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER; -import static jdk.internal.foreign.abi.SharedUtils.alignUp; - -// see vadefs.h (VC header) for the ARM64 va_arg impl -// -// typedef char* va_list; -// -// #define __crt_va_arg(ap, t) \ -// ((sizeof(t) > (2 * sizeof(__int64))) \ -// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ -// : *(t*)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) - _SLOTSIZEOF(t))) -// -public non-sealed class WindowsAArch64VaList implements VaList { - private static final long VA_SLOT_SIZE_BYTES = 8; - private static final VarHandle VH_address = C_POINTER.varHandle(); - - private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL); - - private MemorySegment segment; - - private WindowsAArch64VaList(MemorySegment segment) { - this.segment = segment; - } - - public static VaList empty() { - return EMPTY; - } - - @Override - public int nextVarg(ValueLayout.OfInt layout) { - return (int) read(layout); - } - - @Override - public long nextVarg(ValueLayout.OfLong layout) { - return (long) read(layout); - } - - @Override - public double nextVarg(ValueLayout.OfDouble layout) { - return (double) read(layout); - } - - @Override - public MemorySegment nextVarg(ValueLayout.OfAddress layout) { - return (MemorySegment) read(layout); - } - - @Override - public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(allocator); - return (MemorySegment) read(layout, allocator); - } - - private Object read(MemoryLayout layout) { - return read(layout, SharedUtils.THROWING_ALLOCATOR); - } - - private Object read(MemoryLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(layout); - Object res; - if (layout instanceof GroupLayout) { - TypeClass typeClass = TypeClass.classifyLayout(layout); - res = switch (typeClass) { - case STRUCT_REFERENCE -> { - checkElement(layout, VA_SLOT_SIZE_BYTES); - MemorySegment structAddr = (MemorySegment) VH_address.get(segment); - MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope()); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(struct); - segment = segment.asSlice(VA_SLOT_SIZE_BYTES); - yield seg; - } - case STRUCT_REGISTER, STRUCT_HFA -> { - long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES); - checkElement(layout, size); - MemorySegment struct = allocator.allocate(layout) - .copyFrom(segment.asSlice(0, layout.byteSize())); - segment = segment.asSlice(size); - yield struct; - } - default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); - }; - } else { - checkElement(layout, VA_SLOT_SIZE_BYTES); - VarHandle reader = layout.varHandle(); - res = reader.get(segment); - segment = segment.asSlice(VA_SLOT_SIZE_BYTES); - } - return res; - } - - private static long sizeOf(MemoryLayout layout) { - return switch (TypeClass.classifyLayout(layout)) { - case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES); - default -> VA_SLOT_SIZE_BYTES; - }; - } - - @Override - public void skip(MemoryLayout... layouts) { - Objects.requireNonNull(layouts); - ((MemorySessionImpl) segment.scope()).checkValidState(); - - for (MemoryLayout layout : layouts) { - Objects.requireNonNull(layout); - long size = sizeOf(layout); - checkElement(layout, size); - segment = segment.asSlice(size); - } - } - - private void checkElement(MemoryLayout layout, long size) { - if (segment.byteSize() < size) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - static WindowsAArch64VaList ofAddress(long address, SegmentScope session) { - MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session); - return new WindowsAArch64VaList(segment); - } - - static Builder builder(SegmentScope session) { - return new Builder(session); - } - - @Override - public VaList copy() { - ((MemorySessionImpl) segment.scope()).checkValidState(); - return new WindowsAArch64VaList(segment); - } - - @Override - public MemorySegment segment() { - // make sure that returned segment cannot be accessed - return segment.asSlice(0, 0); - } - - public static non-sealed class Builder implements VaList.Builder { - - private final SegmentScope session; - private final List args = new ArrayList<>(); - - public Builder(SegmentScope session) { - ((MemorySessionImpl) session).checkValidState(); - this.session = session; - } - - private Builder arg(MemoryLayout layout, Object value) { - Objects.requireNonNull(layout); - Objects.requireNonNull(value); - args.add(new SimpleVaArg(layout, value)); - return this; - } - - @Override - public Builder addVarg(ValueLayout.OfInt layout, int value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfLong layout, long value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfDouble layout, double value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(GroupLayout layout, MemorySegment value) { - return arg(layout, value); - } - - public VaList build() { - if (args.isEmpty()) { - return EMPTY; - } - - long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum); - MemorySegment segment = MemorySegment.allocateNative(allocationSize, session); - MemorySegment cursor = segment; - - for (SimpleVaArg arg : args) { - if (arg.layout instanceof GroupLayout) { - MemorySegment msArg = ((MemorySegment) arg.value); - TypeClass typeClass = TypeClass.classifyLayout(arg.layout); - switch (typeClass) { - case STRUCT_REFERENCE -> { - MemorySegment copy = MemorySegment.allocateNative(arg.layout, session); - copy.copyFrom(msArg); // by-value - VH_address.set(cursor, copy); - cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); - } - case STRUCT_REGISTER, STRUCT_HFA -> - cursor = cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize())) - .asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES)); - default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); - } - } else { - VarHandle writer = arg.varHandle(); - writer.set(cursor, arg.value); - cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); - } - } - - return new WindowsAArch64VaList(segment); - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIABI.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIABI.java new file mode 100644 index 00000000000..89062f5c378 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIABI.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, 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.foreign.abi.fallback; + +/** + * enum which maps the {@code ffi_abi} enum + */ +enum FFIABI { + DEFAULT(LibFallback.defaultABI()); + + private final int value; + + FFIABI(int abi) { + this.value = abi; + } + + int value() { + return value; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIStatus.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIStatus.java new file mode 100644 index 00000000000..5ac6a95efa6 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIStatus.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 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.foreign.abi.fallback; + +/** + * See doc: + *

      + * typedef enum { + * FFI_OK = 0, + * FFI_BAD_TYPEDEF, + * FFI_BAD_ABI, + * FFI_BAD_ARGTYPE + * } ffi_status; + */ +enum FFIStatus { + FFI_OK, + FFI_BAD_TYPEDEF, + FFI_BAD_ABI, + FFI_BAD_ARGTYPE; + + static FFIStatus of(int code) { + return switch (code) { + case 0 -> FFI_OK; + case 1 -> FFI_BAD_TYPEDEF; + case 2 -> FFI_BAD_ABI; + case 3 -> FFI_BAD_ARGTYPE; + default -> throw new IllegalArgumentException("Unknown status code: " + code); + }; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java new file mode 100644 index 00000000000..b8e41f763c8 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2023, 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.foreign.abi.fallback; + +import jdk.internal.foreign.Utils; + +import java.lang.foreign.Arena; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.PaddingLayout; +import java.lang.foreign.SequenceLayout; +import java.lang.foreign.StructLayout; +import java.lang.foreign.UnionLayout; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.VarHandle; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; + +/** + * typedef struct _ffi_type + * { + * size_t size; + * unsigned short alignment; + * unsigned short type; + * struct _ffi_type **elements; + * } ffi_type; + */ +class FFIType { + private static final ValueLayout SIZE_T = switch ((int) ADDRESS.bitSize()) { + case 64 -> JAVA_LONG; + case 32 -> JAVA_INT; + default -> throw new IllegalStateException("Address size not supported: " + ADDRESS.byteSize()); + }; + private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT; + private static final StructLayout LAYOUT = Utils.computePaddedStructLayout( + SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements")); + + private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type")); + private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements")); + private static final VarHandle VH_SIZE_T_ARRAY = SIZE_T.arrayElementVarHandle(); + + private static MemorySegment make(List elements, FFIABI abi, Arena scope) { + MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize()); + int i = 0; + for (; i < elements.size(); i++) { + MemoryLayout elementLayout = elements.get(i); + MemorySegment elementType = toFFIType(elementLayout, abi, scope); + elementsSeg.setAtIndex(ADDRESS, i, elementType); + } + // elements array is null-terminated + elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL); + + MemorySegment ffiType = scope.allocate(LAYOUT); + VH_TYPE.set(ffiType, LibFallback.structTag()); + VH_ELEMENTS.set(ffiType, elementsSeg); + + return ffiType; + } + + private static final Map, MemorySegment> CARRIER_TO_TYPE = Map.of( + boolean.class, LibFallback.uint8Type(), + byte.class, LibFallback.sint8Type(), + short.class, LibFallback.sint16Type(), + char.class, LibFallback.uint16Type(), + int.class, LibFallback.sint32Type(), + long.class, LibFallback.sint64Type(), + float.class, LibFallback.floatType(), + double.class, LibFallback.doubleType(), + MemorySegment.class, LibFallback.pointerType() + ); + + static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, Arena scope) { + if (layout instanceof GroupLayout grpl) { + if (grpl instanceof StructLayout strl) { + // libffi doesn't want our padding + List filteredLayouts = strl.memberLayouts().stream() + .filter(Predicate.not(PaddingLayout.class::isInstance)) + .toList(); + MemorySegment structType = make(filteredLayouts, abi, scope); + verifyStructType(strl, filteredLayouts, structType, abi); + return structType; + } + assert grpl instanceof UnionLayout; + // JDK-8301800 + throw new IllegalArgumentException("Fallback linker does not support by-value unions: " + grpl); + } else if (layout instanceof SequenceLayout sl) { + List elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout()); + return make(elements, abi, scope); + } + return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier())); + } + + // verify layout against what libffi sets + private static void verifyStructType(StructLayout structLayout, List filteredLayouts, MemorySegment structType, + FFIABI abi) { + try (Arena verifyArena = Arena.ofConfined()) { + MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size()); + LibFallback.getStructOffsets(structType, offsetsOut, abi); + long expectedOffset = 0; + int offsetIdx = 0; + for (MemoryLayout element : structLayout.memberLayouts()) { + if (!(element instanceof PaddingLayout)) { + long ffiOffset = (long) VH_SIZE_T_ARRAY.get(offsetsOut, offsetIdx++); + if (ffiOffset != expectedOffset) { + throw new IllegalArgumentException("Invalid group layout." + + " Offset of '" + element.name().orElse("") + + "': " + expectedOffset + " != " + ffiOffset); + } + } + expectedOffset += element.byteSize(); + } + } + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java new file mode 100644 index 00000000000..3c73f75bfc0 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2023, 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.foreign.abi.fallback; + +import jdk.internal.foreign.AbstractMemorySegmentImpl; +import jdk.internal.foreign.MemorySessionImpl; +import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.CapturableState; +import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; + +import java.lang.foreign.AddressLayout; +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.ref.Reference; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.invoke.MethodHandles.foldArguments; + +public final class FallbackLinker extends AbstractLinker { + + private static final MethodHandle MH_DO_DOWNCALL; + private static final MethodHandle MH_DO_UPCALL; + + static { + try { + MH_DO_DOWNCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doDowncall", + MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class, FallbackLinker.DowncallData.class)); + MH_DO_UPCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doUpcall", + MethodType.methodType(void.class, MethodHandle.class, MemorySegment.class, MemorySegment.class, UpcallData.class)); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + public static FallbackLinker getInstance() { + class Holder { + static final FallbackLinker INSTANCE = new FallbackLinker(); + } + return Holder.INSTANCE; + } + + public static boolean isSupported() { + return LibFallback.SUPPORTED; + } + + @Override + protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { + MemorySegment cif = makeCif(inferredMethodType, function, FFIABI.DEFAULT, Arena.ofAuto()); + + int capturedStateMask = options.capturedCallState() + .mapToInt(CapturableState::mask) + .reduce(0, (a, b) -> a | b); + DowncallData invData = new DowncallData(cif, function.returnLayout().orElse(null), + function.argumentLayouts(), capturedStateMask); + + MethodHandle target = MethodHandles.insertArguments(MH_DO_DOWNCALL, 2, invData); + + int leadingArguments = 1; // address + MethodType type = inferredMethodType.insertParameterTypes(0, SegmentAllocator.class, MemorySegment.class); + if (capturedStateMask != 0) { + leadingArguments++; + type = type.insertParameterTypes(2, MemorySegment.class); + } + target = target.asCollector(1, Object[].class, inferredMethodType.parameterCount() + leadingArguments); + target = target.asType(type); + target = foldArguments(target, 1, SharedUtils.MH_CHECK_SYMBOL); + target = SharedUtils.swapArguments(target, 0, 1); // normalize parameter order + + return target; + } + + @Override + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + MemorySegment cif = makeCif(targetType, function, FFIABI.DEFAULT, Arena.ofAuto()); + + UpcallData invData = new UpcallData(function.returnLayout().orElse(null), function.argumentLayouts(), cif); + MethodHandle doUpcallMH = MethodHandles.insertArguments(MH_DO_UPCALL, 3, invData); + + return (target, scope) -> { + target = MethodHandles.insertArguments(doUpcallMH, 0, target); + return LibFallback.createClosure(cif, target, scope); + }; + } + + private static MemorySegment makeCif(MethodType methodType, FunctionDescriptor function, FFIABI abi, Arena scope) { + MemorySegment argTypes = scope.allocate(function.argumentLayouts().size() * ADDRESS.byteSize()); + List argLayouts = function.argumentLayouts(); + for (int i = 0; i < argLayouts.size(); i++) { + MemoryLayout layout = argLayouts.get(i); + argTypes.setAtIndex(ADDRESS, i, FFIType.toFFIType(layout, abi, scope)); + } + + MemorySegment returnType = methodType.returnType() != void.class + ? FFIType.toFFIType(function.returnLayout().orElseThrow(), abi, scope) + : LibFallback.voidType(); + return LibFallback.prepCif(returnType, argLayouts.size(), argTypes, abi, scope); + } + + private record DowncallData(MemorySegment cif, MemoryLayout returnLayout, List argLayouts, + int capturedStateMask) {} + + private static Object doDowncall(SegmentAllocator returnAllocator, Object[] args, DowncallData invData) { + List acquiredSessions = new ArrayList<>(); + try (Arena arena = Arena.ofConfined()) { + int argStart = 0; + + MemorySegment target = (MemorySegment) args[argStart++]; + MemorySessionImpl targetImpl = ((AbstractMemorySegmentImpl) target).sessionImpl(); + targetImpl.acquire0(); + acquiredSessions.add(targetImpl); + + MemorySegment capturedState = null; + if (invData.capturedStateMask() != 0) { + capturedState = (MemorySegment) args[argStart++]; + MemorySessionImpl capturedStateImpl = ((AbstractMemorySegmentImpl) capturedState).sessionImpl(); + capturedStateImpl.acquire0(); + acquiredSessions.add(capturedStateImpl); + } + + List argLayouts = invData.argLayouts(); + MemorySegment argPtrs = arena.allocate(argLayouts.size() * ADDRESS.byteSize()); + for (int i = 0; i < argLayouts.size(); i++) { + Object arg = args[argStart + i]; + MemoryLayout layout = argLayouts.get(i); + MemorySegment argSeg = arena.allocate(layout); + writeValue(arg, layout, argSeg, addr -> { + MemorySessionImpl sessionImpl = ((AbstractMemorySegmentImpl) addr).sessionImpl(); + sessionImpl.acquire0(); + acquiredSessions.add(sessionImpl); + }); + argPtrs.setAtIndex(ADDRESS, i, argSeg); + } + + MemorySegment retSeg = null; + if (invData.returnLayout() != null) { + retSeg = (invData.returnLayout() instanceof GroupLayout ? returnAllocator : arena).allocate(invData.returnLayout); + } + + LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs, capturedState, invData.capturedStateMask()); + + Reference.reachabilityFence(invData.cif()); + + return readValue(retSeg, invData.returnLayout()); + } finally { + for (MemorySessionImpl session : acquiredSessions) { + session.release0(); + } + } + } + + // note that cif is not used, but we store it here to keep it alive + private record UpcallData(MemoryLayout returnLayout, List argLayouts, MemorySegment cif) {} + + private static void doUpcall(MethodHandle target, MemorySegment retPtr, MemorySegment argPtrs, UpcallData data) throws Throwable { + List argLayouts = data.argLayouts(); + int numArgs = argLayouts.size(); + MemoryLayout retLayout = data.returnLayout(); + try (Arena upcallArena = Arena.ofConfined()) { + MemorySegment argsSeg = argPtrs.reinterpret(numArgs * ADDRESS.byteSize(), upcallArena, null); + MemorySegment retSeg = retLayout != null + ? retPtr.reinterpret(retLayout.byteSize(), upcallArena, null) + : null; + + Object[] args = new Object[numArgs]; + for (int i = 0; i < numArgs; i++) { + MemoryLayout argLayout = argLayouts.get(i); + MemorySegment argPtr = argsSeg.getAtIndex(ADDRESS, i) + .reinterpret(argLayout.byteSize(), upcallArena, null); + args[i] = readValue(argPtr, argLayout); + } + + Object result = target.invokeWithArguments(args); + + writeValue(result, data.returnLayout(), retSeg); + } + } + + // where + private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg) { + writeValue(arg, layout, argSeg, addr -> {}); + } + + private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg, + Consumer acquireCallback) { + if (layout instanceof ValueLayout.OfBoolean bl) { + argSeg.set(bl, 0, (Boolean) arg); + } else if (layout instanceof ValueLayout.OfByte bl) { + argSeg.set(bl, 0, (Byte) arg); + } else if (layout instanceof ValueLayout.OfShort sl) { + argSeg.set(sl, 0, (Short) arg); + } else if (layout instanceof ValueLayout.OfChar cl) { + argSeg.set(cl, 0, (Character) arg); + } else if (layout instanceof ValueLayout.OfInt il) { + argSeg.set(il, 0, (Integer) arg); + } else if (layout instanceof ValueLayout.OfLong ll) { + argSeg.set(ll, 0, (Long) arg); + } else if (layout instanceof ValueLayout.OfFloat fl) { + argSeg.set(fl, 0, (Float) arg); + } else if (layout instanceof ValueLayout.OfDouble dl) { + argSeg.set(dl, 0, (Double) arg); + } else if (layout instanceof AddressLayout al) { + MemorySegment addrArg = (MemorySegment) arg; + acquireCallback.accept(addrArg); + argSeg.set(al, 0, addrArg); + } else if (layout instanceof GroupLayout) { + argSeg.copyFrom((MemorySegment) arg); // by-value struct + } else { + assert layout == null; + } + } + + private static Object readValue(MemorySegment seg, MemoryLayout layout) { + if (layout instanceof ValueLayout.OfBoolean bl) { + return seg.get(bl, 0); + } else if (layout instanceof ValueLayout.OfByte bl) { + return seg.get(bl, 0); + } else if (layout instanceof ValueLayout.OfShort sl) { + return seg.get(sl, 0); + } else if (layout instanceof ValueLayout.OfChar cl) { + return seg.get(cl, 0); + } else if (layout instanceof ValueLayout.OfInt il) { + return seg.get(il, 0); + } else if (layout instanceof ValueLayout.OfLong ll) { + return seg.get(ll, 0); + } else if (layout instanceof ValueLayout.OfFloat fl) { + return seg.get(fl, 0); + } else if (layout instanceof ValueLayout.OfDouble dl) { + return seg.get(dl, 0); + } else if (layout instanceof AddressLayout al) { + return seg.get(al, 0); + } else if (layout instanceof GroupLayout) { + return seg; + } + assert layout == null; + return null; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java new file mode 100644 index 00000000000..c434dbcef59 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2023, 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.foreign.abi.fallback; + +import jdk.internal.foreign.abi.SharedUtils; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +final class LibFallback { + private LibFallback() {} + + static final boolean SUPPORTED = tryLoadLibrary(); + + private static boolean tryLoadLibrary() { + try { + System.loadLibrary("fallbackLinker"); + } catch (UnsatisfiedLinkError ule) { + return false; + } + init(); + return true; + } + + static int defaultABI() { return NativeConstants.DEFAULT_ABI; } + + static MemorySegment uint8Type() { return NativeConstants.UINT8_TYPE; } + static MemorySegment sint8Type() { return NativeConstants.SINT8_TYPE; } + static MemorySegment uint16Type() { return NativeConstants.UINT16_TYPE; } + static MemorySegment sint16Type() { return NativeConstants.SINT16_TYPE; } + static MemorySegment sint32Type() { return NativeConstants.SINT32_TYPE; } + static MemorySegment sint64Type() { return NativeConstants.SINT64_TYPE; } + static MemorySegment floatType() { return NativeConstants.FLOAT_TYPE; } + static MemorySegment doubleType() { return NativeConstants.DOUBLE_TYPE; } + static MemorySegment pointerType() { return NativeConstants.POINTER_TYPE; } + static MemorySegment voidType() { return NativeConstants.VOID_TYPE; } + + static short structTag() { return NativeConstants.STRUCT_TAG; } + + private static final MethodType UPCALL_TARGET_TYPE = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class); + + /** + * Do a libffi based downcall. This method wraps the {@code ffi_call} function + * + * @param cif a pointer to a {@code ffi_cif} struct + * @param target the address of the target function + * @param retPtr a pointer to a buffer into which the return value shall be written, or {@code null} if the target + * function does not return a value + * @param argPtrs a pointer to an array of pointers, which each point to an argument value + * @param capturedState a pointer to a buffer into which captured state is written, or {@code null} if no state is + * to be captured + * @param capturedStateMask the bit mask indicating which state to capture + * + * @see jdk.internal.foreign.abi.CapturableState + */ + static void doDowncall(MemorySegment cif, MemorySegment target, MemorySegment retPtr, MemorySegment argPtrs, + MemorySegment capturedState, int capturedStateMask) { + doDowncall(cif.address(), target.address(), + retPtr == null ? 0 : retPtr.address(), argPtrs.address(), + capturedState == null ? 0 : capturedState.address(), capturedStateMask); + } + + /** + * Wrapper for {@code ffi_prep_cif} + * + * @param returnType a pointer to an @{code ffi_type} describing the return type + * @param numArgs the number of arguments + * @param paramTypes a pointer to an array of pointers, which each point to an {@code ffi_type} describing a + * parameter type + * @param abi the abi to be used + * @param scope the scope into which to allocate the returned {@code ffi_cif} struct + * @return a pointer to a prepared {@code ffi_cif} struct + * + * @throws IllegalStateException if the call to {@code ffi_prep_cif} returns a non-zero status code + */ + static MemorySegment prepCif(MemorySegment returnType, int numArgs, MemorySegment paramTypes, FFIABI abi, + Arena scope) throws IllegalStateException { + MemorySegment cif = scope.allocate(NativeConstants.SIZEOF_CIF); + checkStatus(ffi_prep_cif(cif.address(), abi.value(), numArgs, returnType.address(), paramTypes.address())); + return cif; + } + + /** + * Create an upcallStub-style closure. This method wraps the {@code ffi_closure_alloc} + * and {@code ffi_prep_closure_loc} functions. + *

      + * The closure will end up calling into {@link #doUpcall(long, long, MethodHandle)} + *

      + * The target method handle should have the type {@code (MemorySegment, MemorySegment) -> void}. The first + * argument is a pointer to the buffer into which the native return value should be written. The second argument + * is a pointer to an array of pointers, which each point to a native argument value. + * + * @param cif a pointer to a {@code ffi_cif} struct + * @param target a method handle that points to the target function + * @param arena the scope to which to attach the created upcall stub + * @return the created upcall stub + * + * @throws IllegalStateException if the call to {@code ffi_prep_closure_loc} returns a non-zero status code + * @throws IllegalArgumentException if {@code target} does not have the right type + */ + static MemorySegment createClosure(MemorySegment cif, MethodHandle target, Arena arena) + throws IllegalStateException, IllegalArgumentException { + if (target.type() != UPCALL_TARGET_TYPE) { + throw new IllegalArgumentException("Target handle has wrong type: " + target.type() + " != " + UPCALL_TARGET_TYPE); + } + + long[] ptrs = new long[3]; + checkStatus(createClosure(cif.address(), target, ptrs)); + long closurePtr = ptrs[0]; + long execPtr = ptrs[1]; + long globalTarget = ptrs[2]; + + return MemorySegment.ofAddress(execPtr).reinterpret(arena, unused -> freeClosure(closurePtr, globalTarget)); + } + + // the target function for a closure call + private static void doUpcall(long retPtr, long argPtrs, MethodHandle target) { + try { + target.invokeExact(MemorySegment.ofAddress(retPtr), MemorySegment.ofAddress(argPtrs)); + } catch (Throwable t) { + SharedUtils.handleUncaughtException(t); + } + } + + /** + * Wrapper for {@code ffi_get_struct_offsets} + * + * @param structType a pointer to an {@code ffi_type} representing a struct + * @param offsetsOut a pointer to an array of {@code size_t}, with one element for each element of the struct. + * This is an 'out' parameter that will be filled in by this call + * @param abi the abi to be used + * + * @throws IllegalStateException if the call to {@code ffi_get_struct_offsets} returns a non-zero status code + */ + static void getStructOffsets(MemorySegment structType, MemorySegment offsetsOut, FFIABI abi) + throws IllegalStateException { + checkStatus(ffi_get_struct_offsets(abi.value(), structType.address(), offsetsOut.address())); + } + + private static void checkStatus(int code) { + FFIStatus status = FFIStatus.of(code); + if (status != FFIStatus.FFI_OK) { + throw new IllegalStateException("libffi call failed with status: " + status); + } + } + + private static native void init(); + + private static native long sizeofCif(); + + private static native int createClosure(long cif, Object userData, long[] ptrs); + private static native void freeClosure(long closureAddress, long globalTarget); + private static native void doDowncall(long cif, long fn, long rvalue, long avalues, long capturedState, int capturedStateMask); + + private static native int ffi_prep_cif(long cif, int abi, int nargs, long rtype, long atypes); + private static native int ffi_get_struct_offsets(int abi, long type, long offsets); + + private static native int ffi_default_abi(); + private static native short ffi_type_struct(); + + private static native long ffi_type_void(); + private static native long ffi_type_uint8(); + private static native long ffi_type_sint8(); + private static native long ffi_type_uint16(); + private static native long ffi_type_sint16(); + private static native long ffi_type_uint32(); + private static native long ffi_type_sint32(); + private static native long ffi_type_uint64(); + private static native long ffi_type_sint64(); + private static native long ffi_type_float(); + private static native long ffi_type_double(); + private static native long ffi_type_pointer(); + + // put these in a separate class to avoid an UnsatisfiedLinkError + // when LibFallback is initialized but the library is not present + private static final class NativeConstants { + private NativeConstants() {} + + static final int DEFAULT_ABI = ffi_default_abi(); + + static final MemorySegment UINT8_TYPE = MemorySegment.ofAddress(ffi_type_uint8()); + static final MemorySegment SINT8_TYPE = MemorySegment.ofAddress(ffi_type_sint8()); + static final MemorySegment UINT16_TYPE = MemorySegment.ofAddress(ffi_type_uint16()); + static final MemorySegment SINT16_TYPE = MemorySegment.ofAddress(ffi_type_sint16()); + static final MemorySegment SINT32_TYPE = MemorySegment.ofAddress(ffi_type_sint32()); + static final MemorySegment SINT64_TYPE = MemorySegment.ofAddress(ffi_type_sint64()); + static final MemorySegment FLOAT_TYPE = MemorySegment.ofAddress(ffi_type_float()); + static final MemorySegment DOUBLE_TYPE = MemorySegment.ofAddress(ffi_type_double()); + static final MemorySegment POINTER_TYPE = MemorySegment.ofAddress(ffi_type_pointer()); + + static final MemorySegment VOID_TYPE = MemorySegment.ofAddress(ffi_type_void()); + static final short STRUCT_TAG = ffi_type_struct(); + static final long SIZEOF_CIF = sizeofCif(); + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/RISCV64Architecture.java b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/RISCV64Architecture.java index a45965e56e1..594a8b31119 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/RISCV64Architecture.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/RISCV64Architecture.java @@ -32,7 +32,7 @@ import jdk.internal.foreign.abi.StubLocations; import jdk.internal.foreign.abi.VMStorage; import jdk.internal.foreign.abi.riscv64.linux.TypeClass; -public class RISCV64Architecture implements Architecture { +public final class RISCV64Architecture implements Architecture { public static final Architecture INSTANCE = new RISCV64Architecture(); private static final short REG64_MASK = 0b0000_0000_0000_0001; @@ -41,6 +41,9 @@ public class RISCV64Architecture implements Architecture { private static final int INTEGER_REG_SIZE = 8; // bytes private static final int FLOAT_REG_SIZE = 8; + // Suppresses default constructor, ensuring non-instantiability. + private RISCV64Architecture() {} + @Override public boolean isStackType(int cls) { return cls == StorageType.STACK; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64CallArranger.java index 93605675a22..29a824afdca 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64CallArranger.java @@ -26,6 +26,7 @@ package jdk.internal.foreign.abi.riscv64.linux; +import java.lang.foreign.AddressLayout; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; @@ -42,7 +43,7 @@ import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.VMStorage; import jdk.internal.foreign.Utils; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.List; @@ -52,7 +53,6 @@ import java.util.Optional; import static jdk.internal.foreign.abi.riscv64.linux.TypeClass.*; import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.*; import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.Regs.*; -import static jdk.internal.foreign.PlatformLayouts.*; /** * For the RISCV64 C ABI specifically, this class uses CallingSequenceBuilder @@ -91,7 +91,7 @@ public class LinuxRISCV64CallArranger { boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); if (returnInMemory) { Class carrier = MemorySegment.class; - MemoryLayout layout = RISCV64.C_POINTER; + MemoryLayout layout = SharedUtils.C_POINTER; csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout, false)); } else if (cDesc.returnLayout().isPresent()) { Class carrier = mt.returnType(); @@ -121,8 +121,8 @@ public class LinuxRISCV64CallArranger { return handle; } - public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) { - Bindings bindings = getBindings(mt, cDesc, true); + public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) { + Bindings bindings = getBindings(mt, cDesc, true, options); final boolean dropReturn = true; /* drop return, since we don't have bindings for it */ return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CLinux, bindings.callingSequence); @@ -391,9 +391,10 @@ public class LinuxRISCV64CallArranger { bindings.vmLoad(storage, carrier); } case POINTER -> { + AddressLayout addressLayout = (AddressLayout) layout; VMStorage storage = storageCalculator.getStorage(StorageType.INTEGER); bindings.vmLoad(storage, long.class) - .boxAddressRaw(Utils.pointeeSize(layout)); + .boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout)); } case STRUCT_REGISTER_X -> { assert carrier == MemorySegment.class; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java index 861e833a0b2..eb11346c137 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java @@ -29,13 +29,9 @@ package jdk.internal.foreign.abi.riscv64.linux; import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; -import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.util.function.Consumer; public final class LinuxRISCV64Linker extends AbstractLinker { @@ -57,21 +53,7 @@ public final class LinuxRISCV64Linker extends AbstractLinker { } @Override - protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) { - return LinuxRISCV64CallArranger.arrangeUpcall(targetType, function); - } - - public static VaList newVaList(Consumer actions, SegmentScope scope) { - LinuxRISCV64VaList.Builder builder = LinuxRISCV64VaList.builder(scope); - actions.accept(builder); - return builder.build(); - } - - public static VaList newVaListOfAddress(long address, SegmentScope scope) { - return LinuxRISCV64VaList.ofAddress(address, scope); - } - - public static VaList emptyVaList() { - return LinuxRISCV64VaList.empty(); + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return LinuxRISCV64CallArranger.arrangeUpcall(targetType, function, options); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64VaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64VaList.java deleted file mode 100644 index 2bcee44ba74..00000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64VaList.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2023, Institute of Software, Chinese Academy of Sciences. - * 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. - * - * 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.foreign.abi.riscv64.linux; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.ValueLayout; -import java.lang.foreign.VaList; -import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.Utils; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static java.lang.foreign.ValueLayout.ADDRESS; -import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; -import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; - -/** - * Standard va_list implementation as defined by RISC-V ABI document and used on Linux. - * In the base integer calling convention, variadic arguments are passed in the same - * manner as named arguments, with one exception. Variadic arguments with 2 * XLEN-bit - * alignment and size at most 2 * XLEN bits are passed in an aligned register pair - * (i.e., the first register in the pair is even-numbered), or on the stack by value - * if none is available. After a variadic argument has been passed on the stack, all - * future arguments will also be passed on the stack (i.e. the last argument register - * may be left unused due to the aligned register pair rule). - */ - -public non-sealed class LinuxRISCV64VaList implements VaList { - // The va_list type is void* on RISCV64. - // See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#cc-type-representations - - private final MemorySegment segment; - private long offset; - - private static final long STACK_SLOT_SIZE = 8; - private static final VaList EMPTY - = new SharedUtils.EmptyVaList(MemorySegment.NULL); - - public static VaList empty() { - return EMPTY; - } - - public LinuxRISCV64VaList(MemorySegment segment, long offset) { - this.segment = segment; - this.offset = offset; - } - - private static LinuxRISCV64VaList readFromAddress(long address, SegmentScope scope) { - MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, scope); // size unknown - return new LinuxRISCV64VaList(segment, 0); - } - - @Override - public int nextVarg(ValueLayout.OfInt layout) { - return (int) read(layout); - } - - @Override - public long nextVarg(ValueLayout.OfLong layout) { - return (long) read(layout); - } - - @Override - public double nextVarg(ValueLayout.OfDouble layout) { - return (double) read(layout); - } - - @Override - public MemorySegment nextVarg(ValueLayout.OfAddress layout) { - return (MemorySegment) read(layout); - } - - @Override - public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(allocator); - return (MemorySegment) read(layout, allocator); - } - - private Object read(MemoryLayout layout) { - return read(layout, THROWING_ALLOCATOR); - } - - private Object read(MemoryLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(layout); - TypeClass typeClass = TypeClass.classifyLayout(layout); - preAlignStack(layout); - - return switch (typeClass) { - case INTEGER, FLOAT, POINTER -> { - checkStackElement(layout); - VarHandle reader = layout.varHandle(); - MemorySegment slice = segment.asSlice(offset, layout.byteSize()); - Object res = reader.get(slice); - postAlignStack(layout); - yield res; - } - case STRUCT_REGISTER_X, STRUCT_REGISTER_F, STRUCT_REGISTER_XF -> { - checkStackElement(layout); - // Struct is passed indirectly via a pointer in an integer register. - MemorySegment slice = segment.asSlice(offset, layout.byteSize()); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(slice); - postAlignStack(layout); - yield seg; - } - case STRUCT_REFERENCE -> { - checkStackElement(ADDRESS); - VarHandle addrReader = ADDRESS.varHandle(); - MemorySegment slice = segment.asSlice(offset, ADDRESS.byteSize()); - MemorySegment addr = (MemorySegment) addrReader.get(slice); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(MemorySegment.ofAddress(addr.address(), layout.byteSize(), segment.scope())); - postAlignStack(ADDRESS); - yield seg; - } - }; - } - - private void checkStackElement(MemoryLayout layout) { - if (Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) > segment.byteSize()) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - private void preAlignStack(MemoryLayout layout) { - if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) { - offset = Utils.alignUp(offset, 16); - } else { - offset = Utils.alignUp(offset, STACK_SLOT_SIZE); - } - } - - private void postAlignStack(MemoryLayout layout) { - if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) { - offset += 16; - } else { - offset += Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE); - } - } - - @Override - public void skip(MemoryLayout... layouts) { - Objects.requireNonNull(layouts); - ((MemorySessionImpl) segment.scope()).checkValidState(); - for (MemoryLayout layout : layouts) { - Objects.requireNonNull(layout); - preAlignStack(layout); - postAlignStack(layout); - } - } - - static LinuxRISCV64VaList.Builder builder(SegmentScope scope) { - return new LinuxRISCV64VaList.Builder(scope); - } - - public static VaList ofAddress(long address, SegmentScope scope) { - return readFromAddress(address, scope); - } - - @Override - public VaList copy() { - MemorySessionImpl sessionImpl = (MemorySessionImpl) segment.scope(); - sessionImpl.checkValidState(); - return new LinuxRISCV64VaList(segment, offset); - } - - @Override - public MemorySegment segment() { - // make sure that returned segment cannot be accessed - return segment.asSlice(0, 0); - } - - public long address() { - return segment.address() + offset; - } - - @Override - public String toString() { - return "LinuxRISCV64VaList{" + "seg: " + address() + ", " + "offset: " + offset + '}'; - } - - public static non-sealed class Builder implements VaList.Builder { - - private final SegmentScope scope; - private final List stackArgs = new ArrayList<>(); - - Builder(SegmentScope scope) { - this.scope = scope; - } - - @Override - public Builder addVarg(ValueLayout.OfInt layout, int value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfLong layout, long value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfDouble layout, double value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(GroupLayout layout, MemorySegment value) { - return arg(layout, value); - } - - private Builder arg(MemoryLayout layout, Object value) { - Objects.requireNonNull(layout); - Objects.requireNonNull(value); - stackArgs.add(new SimpleVaArg(layout, value)); - return this; - } - - boolean isEmpty() { - return stackArgs.isEmpty(); - } - - public VaList build() { - if (isEmpty()) { - return EMPTY; - } - long stackArgsSize = 0; - for (SimpleVaArg arg : stackArgs) { - MemoryLayout layout = arg.layout; - long elementSize = TypeClass.classifyLayout(layout) == TypeClass.STRUCT_REFERENCE ? - ADDRESS.byteSize() : layout.byteSize(); - // arguments with 2 * XLEN-bit alignment and size at most 2 * XLEN bits - // are saved on memory aligned with 2 * XLEN (XLEN=64 for RISCV64). - if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) { - stackArgsSize = Utils.alignUp(stackArgsSize, 16); - elementSize = 16; - } - stackArgsSize += Utils.alignUp(elementSize, STACK_SLOT_SIZE); - } - MemorySegment argsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope); - MemorySegment writeCursor = argsSegment; - for (SimpleVaArg arg : stackArgs) { - MemoryLayout layout; - Object value = arg.value; - if (TypeClass.classifyLayout(arg.layout) == TypeClass.STRUCT_REFERENCE) { - layout = ADDRESS; - } else { - layout = arg.layout; - } - long alignedSize = Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE); - if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) { - writeCursor = Utils.alignUp(writeCursor, 16); - alignedSize = 16; - } - if (layout instanceof GroupLayout) { - writeCursor.copyFrom((MemorySegment) value); - } else { - VarHandle writer = layout.varHandle(); - writer.set(writeCursor, value); - } - writeCursor = writeCursor.asSlice(alignedSize); - } - return new LinuxRISCV64VaList(argsSegment, 0L); - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java index 9a8022eba47..03fc39d0670 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java @@ -31,7 +31,7 @@ import jdk.internal.foreign.abi.VMStorage; import java.util.stream.IntStream; -public class X86_64Architecture implements Architecture { +public final class X86_64Architecture implements Architecture { public static final Architecture INSTANCE = new X86_64Architecture(); private static final short REG8_H_MASK = 0b0000_0000_0000_0010; @@ -48,6 +48,9 @@ public class X86_64Architecture implements Architecture { private static final int VECTOR_REG_SIZE = 16; // size of XMM register private static final int X87_REG_SIZE = 16; + // Suppresses default constructor, ensuring non-instantiability. + private X86_64Architecture() {} + @Override public boolean isStackType(int cls) { return cls == StorageType.STACK; @@ -154,7 +157,9 @@ public class X86_64Architecture implements Architecture { new VMStorage[][] { outputIntRegs, outputVectorRegs, - IntStream.range(0, numX87Outputs).mapToObj(X86_64Architecture::x87Storage).toArray(VMStorage[]::new) + IntStream.range(0, numX87Outputs) + .mapToObj(X86_64Architecture::x87Storage) + .toArray(VMStorage[]::new) }, new VMStorage[][] { volatileIntRegs, diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java index 749872d2776..0a2669c3726 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java @@ -38,18 +38,18 @@ import jdk.internal.foreign.abi.UpcallLinker; import jdk.internal.foreign.abi.VMStorage; import jdk.internal.foreign.abi.x64.X86_64Architecture; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.AddressLayout; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.List; import java.util.Optional; -import static jdk.internal.foreign.PlatformLayouts.SysV; import static jdk.internal.foreign.abi.Binding.vmStore; import static jdk.internal.foreign.abi.x64.X86_64Architecture.*; import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*; @@ -65,6 +65,11 @@ public class CallArranger { private static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6; private static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8; + /** + * The {@code long} native type. + */ + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; + private static final ABIDescriptor CSysV = X86_64Architecture.abiFor( new VMStorage[] { rdi, rsi, rdx, rcx, r8, r9, rax }, new VMStorage[] { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 }, @@ -97,7 +102,7 @@ public class CallArranger { boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); if (returnInMemory) { Class carrier = MemorySegment.class; - MemoryLayout layout = SysV.C_POINTER; + MemoryLayout layout = SharedUtils.C_POINTER; csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); } else if (cDesc.returnLayout().isPresent()) { Class carrier = mt.returnType(); @@ -113,7 +118,7 @@ public class CallArranger { if (!forUpcall) { //add extra binding for number of used vector registers (used for variadic calls) - csb.addArgumentBindings(long.class, SysV.C_LONG, + csb.addArgumentBindings(long.class, C_LONG, List.of(vmStore(rax, long.class))); } @@ -133,8 +138,8 @@ public class CallArranger { return handle; } - public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) { - Bindings bindings = getBindings(mt, cDesc, true); + public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) { + Bindings bindings = getBindings(mt, cDesc, true, options); final boolean dropReturn = true; /* drop return, since we don't have bindings for it */ return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CSysV, bindings.callingSequence); @@ -314,9 +319,10 @@ public class CallArranger { } } case POINTER -> { + AddressLayout addressLayout = (AddressLayout) layout; VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER); bindings.vmLoad(storage, long.class) - .boxAddressRaw(Utils.pointeeSize(layout)); + .boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout)); } case INTEGER -> { VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java deleted file mode 100644 index 1a1fd03dc1f..00000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (c) 2020, 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.foreign.abi.x64.sysv; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.VaList; -import java.lang.foreign.ValueLayout; - -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.Utils; -import jdk.internal.foreign.abi.SharedUtils; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static jdk.internal.foreign.PlatformLayouts.SysV; - -import static java.lang.foreign.MemoryLayout.PathElement.groupElement; -import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; -import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; - -// See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists" -public non-sealed class SysVVaList implements VaList { - -// struct typedef __va_list_tag __va_list_tag { -// unsigned int gp_offset; /* 0 4 */ -// unsigned int fp_offset; /* 4 4 */ -// void * overflow_arg_area; /* 8 8 */ -// void * reg_save_area; /* 16 8 */ -// -// /* size: 24, cachelines: 1, members: 4 */ -// /* last cacheline: 24 bytes */ -// }; - static final GroupLayout LAYOUT = MemoryLayout.structLayout( - SysV.C_INT.withName("gp_offset"), - SysV.C_INT.withName("fp_offset"), - SysV.C_POINTER.withName("overflow_arg_area"), - SysV.C_POINTER.withName("reg_save_area") - ).withName("__va_list_tag"); - - private static final long STACK_SLOT_SIZE = 8; - - private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64); - private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128); - - private static final GroupLayout LAYOUT_REG_SAVE_AREA = MemoryLayout.structLayout( - GP_REG.withName("%rdi"), - GP_REG.withName("%rsi"), - GP_REG.withName("%rdx"), - GP_REG.withName("%rcx"), - GP_REG.withName("%r8"), - GP_REG.withName("%r9"), - FP_REG.withName("%xmm0"), - FP_REG.withName("%xmm1"), - FP_REG.withName("%xmm2"), - FP_REG.withName("%xmm3"), - FP_REG.withName("%xmm4"), - FP_REG.withName("%xmm5"), - FP_REG.withName("%xmm6"), - FP_REG.withName("%xmm7") -// specification and implementation differ as to whether the following are part of a reg save area -// Let's go with the implementation, since then it actually works :) -// FP_REG.withName("%xmm8"), -// FP_REG.withName("%xmm9"), -// FP_REG.withName("%xmm10"), -// FP_REG.withName("%xmm11"), -// FP_REG.withName("%xmm12"), -// FP_REG.withName("%xmm13"), -// FP_REG.withName("%xmm14"), -// FP_REG.withName("%xmm15") - ); - - private static final long FP_OFFSET = LAYOUT_REG_SAVE_AREA.byteOffset(groupElement("%xmm0")); - - private static final int GP_SLOT_SIZE = (int) GP_REG.byteSize(); - private static final int FP_SLOT_SIZE = (int) FP_REG.byteSize(); - - private static final int MAX_GP_OFFSET = (int) FP_OFFSET; // 6 regs used - private static final int MAX_FP_OFFSET = (int) LAYOUT_REG_SAVE_AREA.byteSize(); // 8 16 byte regs - - private static final VarHandle VH_fp_offset = LAYOUT.varHandle(groupElement("fp_offset")); - private static final VarHandle VH_gp_offset = LAYOUT.varHandle(groupElement("gp_offset")); - private static final VarHandle VH_overflow_arg_area = LAYOUT.varHandle(groupElement("overflow_arg_area")); - private static final VarHandle VH_reg_save_area = LAYOUT.varHandle(groupElement("reg_save_area")); - - private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress()); - - private final MemorySegment segment; - private MemorySegment overflowArgArea; - private final MemorySegment regSaveArea; - private final long gpLimit; - private final long fpLimit; - - private SysVVaList(MemorySegment segment, - MemorySegment overflowArgArea, - MemorySegment regSaveArea, long gpLimit, long fpLimit) { - this.segment = segment; - this.overflowArgArea = overflowArgArea; - this.regSaveArea = regSaveArea; - this.gpLimit = gpLimit; - this.fpLimit = fpLimit; - } - - private static SysVVaList readFromAddress(long address, SegmentScope scope) { - MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope); - MemorySegment regSaveArea = getRegSaveArea(segment); - MemorySegment overflowArgArea = getArgOverflowArea(segment); - return new SysVVaList(segment, overflowArgArea, regSaveArea, MAX_GP_OFFSET, MAX_FP_OFFSET); - } - - private static MemorySegment emptyListAddress() { - MemorySegment base = MemorySegment.allocateNative(LAYOUT, SegmentScope.global()); - VH_gp_offset.set(base, MAX_GP_OFFSET); - VH_fp_offset.set(base, MAX_FP_OFFSET); - VH_overflow_arg_area.set(base, MemorySegment.NULL); - VH_reg_save_area.set(base, MemorySegment.NULL); - return base.asSlice(0, 0); - } - - public static VaList empty() { - return EMPTY; - } - - private int currentGPOffset() { - return (int) VH_gp_offset.get(segment); - } - - private void currentGPOffset(int i) { - VH_gp_offset.set(segment, i); - } - - private int currentFPOffset() { - return (int) VH_fp_offset.get(segment); - } - - private void currentFPOffset(int i) { - VH_fp_offset.set(segment, i); - } - - private static MemorySegment getRegSaveArea(MemorySegment segment) { - return ((MemorySegment)VH_reg_save_area.get(segment)) - .asSlice(0, LAYOUT_REG_SAVE_AREA.byteSize()); - } - - private static MemorySegment getArgOverflowArea(MemorySegment segment) { - return (MemorySegment)VH_overflow_arg_area.get(segment); // size unknown - } - - private long preAlignOffset(MemoryLayout layout) { - long alignmentOffset = 0; - if (layout.byteAlignment() > STACK_SLOT_SIZE) { - long addr = overflowArgArea.address(); - alignmentOffset = Utils.alignUp(addr, 16) - addr; - } - return alignmentOffset; - } - - private void setOverflowArgArea(MemorySegment newSegment) { - overflowArgArea = newSegment; - VH_overflow_arg_area.set(segment, overflowArgArea); - } - - private void preAlignStack(MemoryLayout layout) { - setOverflowArgArea(overflowArgArea.asSlice(preAlignOffset(layout))); - } - - private void postAlignStack(MemoryLayout layout) { - setOverflowArgArea(overflowArgArea.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE))); - } - - @Override - public int nextVarg(ValueLayout.OfInt layout) { - return (int) read(layout); - } - - @Override - public long nextVarg(ValueLayout.OfLong layout) { - return (long) read(layout); - } - - @Override - public double nextVarg(ValueLayout.OfDouble layout) { - return (double) read(layout); - } - - @Override - public MemorySegment nextVarg(ValueLayout.OfAddress layout) { - return (MemorySegment) read(layout); - } - - @Override - public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(allocator); - return (MemorySegment) read(layout, allocator); - } - - private Object read(MemoryLayout layout) { - return read(layout, THROWING_ALLOCATOR); - } - - private Object read(MemoryLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(layout); - TypeClass typeClass = TypeClass.classifyLayout(layout); - if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass) - || typeClass.inMemory()) { - checkStackElement(layout); - preAlignStack(layout); - return switch (typeClass.kind()) { - case STRUCT -> { - MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize()); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(slice); - postAlignStack(layout); - yield seg; - } - case POINTER, INTEGER, FLOAT -> { - VarHandle reader = layout.varHandle(); - MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize()); - Object res = reader.get(slice); - postAlignStack(layout); - yield res; - } - }; - } else { - checkRegSaveAreaElement(layout, typeClass); - return switch (typeClass.kind()) { - case STRUCT -> { - MemorySegment value = allocator.allocate(layout); - int classIdx = 0; - long offset = 0; - while (offset < layout.byteSize()) { - final long copy = Math.min(layout.byteSize() - offset, 8); - boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE; - if (isSSE) { - MemorySegment.copy(regSaveArea, currentFPOffset(), value, offset, copy); - currentFPOffset(currentFPOffset() + FP_SLOT_SIZE); - } else { - MemorySegment.copy(regSaveArea, currentGPOffset(), value, offset, copy); - currentGPOffset(currentGPOffset() + GP_SLOT_SIZE); - } - offset += copy; - } - yield value; - } - case POINTER, INTEGER -> { - VarHandle reader = layout.varHandle(); - Object res = reader.get(regSaveArea.asSlice(currentGPOffset())); - currentGPOffset(currentGPOffset() + GP_SLOT_SIZE); - yield res; - } - case FLOAT -> { - VarHandle reader = layout.varHandle(); - Object res = reader.get(regSaveArea.asSlice(currentFPOffset())); - currentFPOffset(currentFPOffset() + FP_SLOT_SIZE); - yield res; - } - }; - } - } - - private void checkRegSaveAreaElement(MemoryLayout layout, TypeClass typeClass) { - long gpSize = typeClass.nIntegerRegs() * GP_SLOT_SIZE; - long fpSize = typeClass.nVectorRegs() * FP_SLOT_SIZE; - if (currentGPOffset() + gpSize > gpLimit - || currentFPOffset() + fpSize > fpLimit) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - private void checkStackElement(MemoryLayout layout) { - long offset = preAlignOffset(layout); - if (offset + layout.byteSize() > overflowArgArea.byteSize()) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - @Override - public void skip(MemoryLayout... layouts) { - Objects.requireNonNull(layouts); - ((MemorySessionImpl) segment.scope()).checkValidState(); - for (MemoryLayout layout : layouts) { - Objects.requireNonNull(layout); - TypeClass typeClass = TypeClass.classifyLayout(layout); - if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)) { - checkStackElement(layout); - preAlignStack(layout); - postAlignStack(layout); - } else { - checkRegSaveAreaElement(layout, typeClass); - currentGPOffset(currentGPOffset() + (((int) typeClass.nIntegerRegs()) * GP_SLOT_SIZE)); - currentFPOffset(currentFPOffset() + (((int) typeClass.nVectorRegs()) * FP_SLOT_SIZE)); - } - } - } - - static SysVVaList.Builder builder(SegmentScope scope) { - return new SysVVaList.Builder(scope); - } - - public static VaList ofAddress(long address, SegmentScope scope) { - return readFromAddress(address, scope); - } - - @Override - public VaList copy() { - MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope()); - copy.copyFrom(segment); - return new SysVVaList(copy, overflowArgArea, regSaveArea, gpLimit, fpLimit); - } - - @Override - public MemorySegment segment() { - // make sure that returned segment cannot be accessed - return segment.asSlice(0, 0); - } - - private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset, TypeClass typeClass) { - return currentGPOffset > MAX_GP_OFFSET - typeClass.nIntegerRegs() * GP_SLOT_SIZE - || currentFPOffset > MAX_FP_OFFSET - typeClass.nVectorRegs() * FP_SLOT_SIZE; - } - - @Override - public String toString() { - return "SysVVaList{" - + "gp_offset=" + currentGPOffset() - + ", fp_offset=" + currentFPOffset() - + ", overflow_arg_area=" + overflowArgArea - + ", reg_save_area=" + regSaveArea - + '}'; - } - - public static non-sealed class Builder implements VaList.Builder { - private final SegmentScope scope; - private final MemorySegment reg_save_area; - private long currentGPOffset = 0; - private long currentFPOffset = FP_OFFSET; - private final List stackArgs = new ArrayList<>(); - - public Builder(SegmentScope scope) { - this.scope = scope; - this.reg_save_area = MemorySegment.allocateNative(LAYOUT_REG_SAVE_AREA, scope); - } - - @Override - public Builder addVarg(ValueLayout.OfInt layout, int value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfLong layout, long value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfDouble layout, double value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(GroupLayout layout, MemorySegment value) { - return arg(layout, value); - } - - private Builder arg(MemoryLayout layout, Object value) { - Objects.requireNonNull(layout); - Objects.requireNonNull(value); - TypeClass typeClass = TypeClass.classifyLayout(layout); - if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass) - || typeClass.inMemory()) { - // stack it! - stackArgs.add(new SimpleVaArg(layout, value)); - } else { - switch (typeClass.kind()) { - case STRUCT -> { - MemorySegment valueSegment = (MemorySegment) value; - int classIdx = 0; - long offset = 0; - while (offset < layout.byteSize()) { - final long copy = Math.min(layout.byteSize() - offset, 8); - boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE; - if (isSSE) { - MemorySegment.copy(valueSegment, offset, reg_save_area, currentFPOffset, copy); - currentFPOffset += FP_SLOT_SIZE; - } else { - MemorySegment.copy(valueSegment, offset, reg_save_area, currentGPOffset, copy); - currentGPOffset += GP_SLOT_SIZE; - } - offset += copy; - } - } - case POINTER, INTEGER -> { - VarHandle writer = layout.varHandle(); - writer.set(reg_save_area.asSlice(currentGPOffset), value); - currentGPOffset += GP_SLOT_SIZE; - } - case FLOAT -> { - VarHandle writer = layout.varHandle(); - writer.set(reg_save_area.asSlice(currentFPOffset), value); - currentFPOffset += FP_SLOT_SIZE; - } - } - } - return this; - } - - private boolean isEmpty() { - return currentGPOffset == 0 && currentFPOffset == FP_OFFSET && stackArgs.isEmpty(); - } - - public VaList build() { - if (isEmpty()) { - return EMPTY; - } - - MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, scope); - MemorySegment stackArgsSegment; - if (!stackArgs.isEmpty()) { - long stackArgsSize = stackArgs.stream().reduce(0L, - (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum); - stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope); - MemorySegment writeCursor = stackArgsSegment; - for (SimpleVaArg arg : stackArgs) { - if (arg.layout.byteSize() > 8) { - writeCursor = Utils.alignUp(writeCursor, Math.min(16, arg.layout.byteSize())); - } - if (arg.layout instanceof GroupLayout) { - writeCursor.copyFrom((MemorySegment) arg.value); - } else { - VarHandle writer = arg.varHandle(); - writer.set(writeCursor, arg.value); - } - writeCursor = writeCursor.asSlice(Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE)); - } - } else { - stackArgsSegment = MemorySegment.NULL; - } - - VH_fp_offset.set(vaListSegment, (int) FP_OFFSET); - VH_overflow_arg_area.set(vaListSegment, stackArgsSegment); - VH_reg_save_area.set(vaListSegment, reg_save_area); - assert MemorySessionImpl.sameOwnerThread(reg_save_area.scope(), vaListSegment.scope()); - return new SysVVaList(vaListSegment, stackArgsSegment, reg_save_area, currentGPOffset, currentFPOffset); - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java index 19410f500a6..e61b743526c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java @@ -28,12 +28,9 @@ package jdk.internal.foreign.abi.x64.sysv; import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; -import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.util.function.Consumer; /** * ABI implementation based on System V ABI AMD64 supplement v.0.99.6 @@ -58,21 +55,7 @@ public final class SysVx64Linker extends AbstractLinker { } @Override - protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) { - return CallArranger.arrangeUpcall(targetType, function); - } - - public static VaList newVaList(Consumer actions, SegmentScope scope) { - SysVVaList.Builder builder = SysVVaList.builder(scope); - actions.accept(builder); - return builder.build(); - } - - public static VaList newVaListOfAddress(long address, SegmentScope scope) { - return SysVVaList.ofAddress(address, scope); - } - - public static VaList emptyVaList() { - return SysVVaList.empty(); + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.arrangeUpcall(targetType, function, options); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java index 209830a354e..f905dae0fe5 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java @@ -37,17 +37,17 @@ import jdk.internal.foreign.abi.UpcallLinker; import jdk.internal.foreign.abi.VMStorage; import jdk.internal.foreign.abi.x64.X86_64Architecture; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.AddressLayout; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.List; import java.util.Optional; -import static jdk.internal.foreign.PlatformLayouts.Win64; import static jdk.internal.foreign.abi.x64.X86_64Architecture.*; import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*; @@ -104,7 +104,7 @@ public class CallArranger { boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); if (returnInMemory) { Class carrier = MemorySegment.class; - MemoryLayout layout = Win64.C_POINTER; + MemoryLayout layout = SharedUtils.C_POINTER; csb.addArgumentBindings(carrier, layout, false); if (forUpcall) { csb.setReturnBindings(carrier, layout); @@ -132,8 +132,8 @@ public class CallArranger { return handle; } - public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) { - Bindings bindings = getBindings(mt, cDesc, true); + public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) { + Bindings bindings = getBindings(mt, cDesc, true, options); final boolean dropReturn = false; /* need the return value as well */ return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CWindows, bindings.callingSequence); @@ -265,9 +265,10 @@ public class CallArranger { .boxAddress(layout); } case POINTER -> { + AddressLayout addressLayout = (AddressLayout) layout; VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER); bindings.vmLoad(storage, long.class) - .boxAddressRaw(Utils.pointeeSize(layout)); + .boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout)); } case INTEGER -> { VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java deleted file mode 100644 index 6e14248176e..00000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2020, 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.foreign.abi.x64.windows; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.VaList; -import java.lang.foreign.ValueLayout; - -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER; - -// see vadefs.h (VC header) -// -// in short -// -> va_list is just a pointer to a buffer with 64 bit entries. -// -> non-power-of-two-sized, or larger than 64 bit types passed by reference. -// -> other types passed in 64 bit slots by normal function calling convention. -// -// X64 va_arg impl: -// -// typedef char* va_list; -// -// #define __crt_va_arg(ap, t) \ -// ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \ -// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ -// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64))) -// -public non-sealed class WinVaList implements VaList { - private static final long VA_SLOT_SIZE_BYTES = 8; - private static final VarHandle VH_address = C_POINTER.varHandle(); - - private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL); - - private MemorySegment segment; - - private WinVaList(MemorySegment segment) { - this.segment = segment; - } - - public static final VaList empty() { - return EMPTY; - } - - @Override - public int nextVarg(ValueLayout.OfInt layout) { - return (int) read(layout); - } - - @Override - public long nextVarg(ValueLayout.OfLong layout) { - return (long) read(layout); - } - - @Override - public double nextVarg(ValueLayout.OfDouble layout) { - return (double) read(layout); - } - - @Override - public MemorySegment nextVarg(ValueLayout.OfAddress layout) { - return (MemorySegment) read(layout); - } - - @Override - public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(allocator); - return (MemorySegment) read(layout, allocator); - } - - private Object read(MemoryLayout layout) { - return read(layout, SharedUtils.THROWING_ALLOCATOR); - } - - private Object read(MemoryLayout layout, SegmentAllocator allocator) { - Objects.requireNonNull(layout); - Object res; - checkElement(layout); - if (layout instanceof GroupLayout) { - TypeClass typeClass = TypeClass.typeClassFor(layout, false); - res = switch (typeClass) { - case STRUCT_REFERENCE -> { - MemorySegment structAddr = (MemorySegment) VH_address.get(segment); - MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope()); - MemorySegment seg = allocator.allocate(layout); - seg.copyFrom(struct); - yield seg; - } - case STRUCT_REGISTER -> - allocator.allocate(layout).copyFrom(segment.asSlice(0, layout.byteSize())); - default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); - }; - } else { - VarHandle reader = layout.varHandle(); - res = reader.get(segment); - } - segment = segment.asSlice(VA_SLOT_SIZE_BYTES); - return res; - } - - private void checkElement(MemoryLayout layout) { - if (segment.byteSize() < VA_SLOT_SIZE_BYTES) { - throw SharedUtils.newVaListNSEE(layout); - } - } - - @Override - public void skip(MemoryLayout... layouts) { - Objects.requireNonNull(layouts); - ((MemorySessionImpl) segment.scope()).checkValidState(); - for (MemoryLayout layout : layouts) { - Objects.requireNonNull(layout); - checkElement(layout); - segment = segment.asSlice(VA_SLOT_SIZE_BYTES); - } - } - - static WinVaList ofAddress(long address, SegmentScope scope) { - return new WinVaList(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope)); - } - - static Builder builder(SegmentScope scope) { - return new Builder(scope); - } - - @Override - public VaList copy() { - ((MemorySessionImpl) segment.scope()).checkValidState(); - return new WinVaList(segment); - } - - @Override - public MemorySegment segment() { - // make sure that returned segment cannot be accessed - return segment.asSlice(0, 0); - } - - public static non-sealed class Builder implements VaList.Builder { - - private final SegmentScope scope; - private final List args = new ArrayList<>(); - - public Builder(SegmentScope scope) { - ((MemorySessionImpl) scope).checkValidState(); - this.scope = scope; - } - - private Builder arg(MemoryLayout layout, Object value) { - Objects.requireNonNull(layout); - Objects.requireNonNull(value); - args.add(new SimpleVaArg(layout, value)); - return this; - } - - @Override - public Builder addVarg(ValueLayout.OfInt layout, int value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfLong layout, long value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfDouble layout, double value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { - return arg(layout, value); - } - - @Override - public Builder addVarg(GroupLayout layout, MemorySegment value) { - return arg(layout, value); - } - - public VaList build() { - if (args.isEmpty()) { - return EMPTY; - } - - MemorySegment segment = MemorySegment.allocateNative(VA_SLOT_SIZE_BYTES * args.size(), scope); - MemorySegment cursor = segment; - - for (SimpleVaArg arg : args) { - if (arg.layout instanceof GroupLayout) { - MemorySegment msArg = ((MemorySegment) arg.value); - TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false); - switch (typeClass) { - case STRUCT_REFERENCE -> { - MemorySegment copy = MemorySegment.allocateNative(arg.layout, scope); - copy.copyFrom(msArg); // by-value - VH_address.set(cursor, copy); - } - case STRUCT_REGISTER -> - cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES)); - default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); - } - } else { - VarHandle writer = arg.varHandle(); - writer.set(cursor, arg.value); - } - cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); - } - - return new WinVaList(segment); - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java index 9bf3b30ea0c..16cf325f6e6 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java @@ -28,12 +28,8 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.util.function.Consumer; /** * ABI implementation based on Windows ABI AMD64 supplement v.0.99.6 @@ -58,21 +54,7 @@ public final class Windowsx64Linker extends AbstractLinker { } @Override - protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) { - return CallArranger.arrangeUpcall(targetType, function); - } - - public static VaList newVaList(Consumer actions, SegmentScope scope) { - WinVaList.Builder builder = WinVaList.builder(scope); - actions.accept(builder); - return builder.build(); - } - - public static VaList newVaListOfAddress(long address, SegmentScope scope) { - return WinVaList.ofAddress(address, scope); - } - - public static VaList emptyVaList() { - return WinVaList.empty(); + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.arrangeUpcall(targetType, function, options); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java index 7bb78eabd84..2aa5e8812ee 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -49,15 +49,13 @@ public sealed abstract class AbstractGroupLayout elements; + final long minBitAlignment; - AbstractGroupLayout(Kind kind, List elements) { - this(kind, elements, kind.alignof(elements), Optional.empty()); - } - - AbstractGroupLayout(Kind kind, List elements, long bitAlignment, Optional name) { - super(kind.sizeof(elements), bitAlignment, name); // Subclassing creates toctou problems here + AbstractGroupLayout(Kind kind, List elements, long bitSize, long bitAlignment, long minBitAlignment, Optional name) { + super(bitSize, bitAlignment, name); // Subclassing creates toctou problems here this.kind = kind; this.elements = List.copyOf(elements); + this.minBitAlignment = minBitAlignment; } /** @@ -83,20 +81,24 @@ public sealed abstract class AbstractGroupLayout otherGroup && - kind == otherGroup.kind && - elements.equals(otherGroup.elements); + return this == other || + other instanceof AbstractGroupLayout otherGroup && + super.equals(other) && + kind == otherGroup.kind && + elements.equals(otherGroup.elements); } /** @@ -109,7 +111,7 @@ public sealed abstract class AbstractGroupLayout elems) { - long size = 0; - for (MemoryLayout elem : elems) { - size = sizeOp.applyAsLong(size, elem.bitSize()); - } - return size; - } - - long alignof(List elems) { - return elems.stream() - .mapToLong(MemoryLayout::bitAlignment) - .max() // max alignment in case we have member layouts - .orElse(1); // or minimal alignment if no member layout is given } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java index 48c64eb7fc4..ee109546d63 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -25,10 +25,6 @@ */ package jdk.internal.foreign.layout; -import jdk.internal.foreign.Utils; -import jdk.internal.vm.annotation.ForceInline; -import jdk.internal.vm.annotation.Stable; - import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.SequenceLayout; @@ -41,51 +37,50 @@ import java.util.Optional; public abstract sealed class AbstractLayout & MemoryLayout> permits AbstractGroupLayout, PaddingLayoutImpl, SequenceLayoutImpl, ValueLayouts.AbstractValueLayout { - private final long bitSize; - private final long bitAlignment; + private final long byteSize; + private final long byteAlignment; private final Optional name; - @Stable - private long byteSize; AbstractLayout(long bitSize, long bitAlignment, Optional name) { - this.bitSize = bitSize; - this.bitAlignment = bitAlignment; - this.name = name; + this.byteSize = MemoryLayoutUtil.requireBitSizeValid(bitSize, true) / 8; + this.byteAlignment = requirePowerOfTwoAndGreaterOrEqualToEight(bitAlignment) / 8; + this.name = Objects.requireNonNull(name); } public final L withName(String name) { - Objects.requireNonNull(name); - return dup(bitAlignment, Optional.of(name)); + return dup(bitAlignment(), Optional.of(name)); + } + + public final L withoutName() { + return dup(bitAlignment(), Optional.empty()); } public final Optional name() { return name; } - public final L withBitAlignment(long bitAlignment) { - checkAlignment(bitAlignment); + public L withBitAlignment(long bitAlignment) { return dup(bitAlignment, name); } public final long bitAlignment() { - return bitAlignment; + return byteAlignment * 8; + } + + public final long byteAlignment() { + return byteAlignment; } - @ForceInline public final long byteSize() { - if (byteSize == 0) { - byteSize = Utils.bitsToBytesOrThrow(bitSize(), - () -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8")); - } return byteSize; } public final long bitSize() { - return bitSize; + return byteSize * 8; } public boolean hasNaturalAlignment() { - return bitSize == bitAlignment; + return byteSize == byteAlignment; } // the following methods have to copy the same Javadoc as in MemoryLayout, or subclasses will just show @@ -96,7 +91,7 @@ public abstract sealed class AbstractLayout & Memory */ @Override public int hashCode() { - return Objects.hash(name, bitSize, bitAlignment); + return Objects.hash(name, byteSize, byteAlignment); } /** @@ -118,39 +113,36 @@ public abstract sealed class AbstractLayout & Memory */ @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - return other instanceof AbstractLayout otherLayout && name.equals(otherLayout.name) && - bitSize == otherLayout.bitSize && - bitAlignment == otherLayout.bitAlignment; + byteSize == otherLayout.byteSize && + byteAlignment == otherLayout.byteAlignment; } /** * {@return the string representation of this layout} */ + @Override public abstract String toString(); - abstract L dup(long alignment, Optional name); + abstract L dup(long bitAlignment, Optional name); String decorateLayoutString(String s) { if (name().isPresent()) { s = String.format("%s(%s)", s, name().get()); } if (!hasNaturalAlignment()) { - s = bitAlignment + "%" + s; + s = bitAlignment() + "%" + s; } return s; } - private static void checkAlignment(long alignmentBitCount) { - if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two - (alignmentBitCount < 8)) { //alignment must be greater than 8 - throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount); + private static long requirePowerOfTwoAndGreaterOrEqualToEight(long value) { + if (((value & (value - 1)) != 0L) || // value must be a power of two + (value < 8)) { // value must be greater or equal to 8 + throw new IllegalArgumentException("Invalid alignment: " + value); } + return value; } - } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java b/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java index fa154bd09ba..ec093278cf1 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -30,14 +30,18 @@ public final class MemoryLayoutUtil { private MemoryLayoutUtil() { } - public static void checkSize(long size) { - checkSize(size, false); + public static long requireNonNegative(long value) { + if (value < 0) { + throw new IllegalArgumentException("The provided value was negative: " + value); + } + return value; } - public static void checkSize(long size, boolean includeZero) { - if (size < 0 || (!includeZero && size == 0)) { - throw new IllegalArgumentException("Invalid size for layout: " + size); + public static long requireBitSizeValid(long bitSize, boolean allowZero) { + if ((bitSize == 0 && !allowZero) || bitSize < 0 || bitSize % 8 != 0) { + throw new IllegalArgumentException("Invalid bitSize: " + bitSize); } + return bitSize; } } \ No newline at end of file diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java index 3559a8d0f29..2e82e19dfa1 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -32,7 +32,7 @@ import java.util.Optional; public final class PaddingLayoutImpl extends AbstractLayout implements PaddingLayout { private PaddingLayoutImpl(long bitSize) { - this(bitSize, 1, Optional.empty()); + this(bitSize, 8, Optional.empty()); } private PaddingLayoutImpl(long bitSize, long bitAlignment, Optional name) { @@ -46,16 +46,10 @@ public final class PaddingLayoutImpl extends AbstractLayout i @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!super.equals(other)) { - return false; - } - if (!(other instanceof PaddingLayoutImpl p)) { - return false; - } - return bitSize() == p.bitSize(); + return this == other || + other instanceof PaddingLayoutImpl otherPadding && + super.equals(other) && + bitSize() == otherPadding.bitSize(); } @Override diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java index c249f76f9ee..00fc77c6047 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -68,7 +68,6 @@ public final class SequenceLayoutImpl extends AbstractLayout * @throws IllegalArgumentException if {@code elementCount < 0}. */ public SequenceLayout withElementCount(long elementCount) { - MemoryLayoutUtil.checkSize(elementCount, true); return new SequenceLayoutImpl(elementCount, elementLayout, bitAlignment(), name()); } @@ -177,21 +176,18 @@ public final class SequenceLayoutImpl extends AbstractLayout @Override public String toString() { + boolean max = (Long.MAX_VALUE / elementLayout.bitSize()) == elemCount; return decorateLayoutString(String.format("[%s:%s]", - elemCount, elementLayout)); + max ? "*" : elemCount, elementLayout)); } @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!super.equals(other)) { - return false; - } - return other instanceof SequenceLayoutImpl otherSeq && - elemCount == otherSeq.elemCount && - elementLayout.equals(otherSeq.elementLayout); + return this == other || + other instanceof SequenceLayoutImpl otherSeq && + super.equals(other) && + elemCount == otherSeq.elemCount && + elementLayout.equals(otherSeq.elementLayout); } @Override @@ -204,6 +200,14 @@ public final class SequenceLayoutImpl extends AbstractLayout return new SequenceLayoutImpl(elementCount(), elementLayout, bitAlignment, name); } + @Override + public SequenceLayoutImpl withBitAlignment(long bitAlignment) { + if (bitAlignment < elementLayout.bitAlignment()) { + throw new IllegalArgumentException("Invalid alignment constraint"); + } + return super.withBitAlignment(bitAlignment); + } + @Override public boolean hasNaturalAlignment() { return bitAlignment() == elementLayout.bitAlignment(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java index 339afd735bb..a286f79a02f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java @@ -32,21 +32,26 @@ import java.util.Optional; public final class StructLayoutImpl extends AbstractGroupLayout implements StructLayout { - private StructLayoutImpl(List elements) { - super(Kind.STRUCT, elements); - } - - private StructLayoutImpl(List elements, long bitAlignment, Optional name) { - super(Kind.STRUCT, elements, bitAlignment, name); + private StructLayoutImpl(List elements, long bitSize, long bitAlignment, long minBitAlignment, Optional name) { + super(Kind.STRUCT, elements, bitSize, bitAlignment, minBitAlignment, name); } @Override StructLayoutImpl dup(long bitAlignment, Optional name) { - return new StructLayoutImpl(memberLayouts(), bitAlignment, name); + return new StructLayoutImpl(memberLayouts(), bitSize(), bitAlignment, minBitAlignment, name); } public static StructLayout of(List elements) { - return new StructLayoutImpl(elements); + long size = 0; + long align = 8; + for (MemoryLayout elem : elements) { + if (size % elem.bitAlignment() != 0) { + throw new IllegalArgumentException("Invalid alignment constraint for member layout: " + elem); + } + size = Math.addExact(size, elem.bitSize()); + align = Math.max(align, elem.bitAlignment()); + } + return new StructLayoutImpl(elements, size, align, align, Optional.empty()); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java index d8adb186c62..64b960028a8 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java @@ -32,21 +32,23 @@ import java.util.Optional; public final class UnionLayoutImpl extends AbstractGroupLayout implements UnionLayout { - private UnionLayoutImpl(List elements) { - super(Kind.UNION, elements); - } - - private UnionLayoutImpl(List elements, long bitAlignment, Optional name) { - super(Kind.UNION, elements, bitAlignment, name); + private UnionLayoutImpl(List elements, long bitSize, long bitAlignment, long minBitAlignment, Optional name) { + super(Kind.UNION, elements, bitSize, bitAlignment, minBitAlignment, name); } @Override UnionLayoutImpl dup(long bitAlignment, Optional name) { - return new UnionLayoutImpl(memberLayouts(), bitAlignment, name); + return new UnionLayoutImpl(memberLayouts(), bitSize(), bitAlignment, minBitAlignment, name); } public static UnionLayout of(List elements) { - return new UnionLayoutImpl(elements); + long size = 0; + long align = 8; + for (MemoryLayout elem : elements) { + size = Math.max(size, elem.bitSize()); + align = Math.max(align, elem.bitAlignment()); + } + return new UnionLayoutImpl(elements, size, align, align, Optional.empty()); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java index 079cb123a39..67e7310d880 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -35,6 +35,7 @@ import sun.invoke.util.Wrapper; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; +import java.lang.foreign.AddressLayout; import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; @@ -58,8 +59,8 @@ import java.util.Optional; */ public final class ValueLayouts { - private ValueLayouts() { - } + // Suppresses default constructor, ensuring non-instantiability. + private ValueLayouts() {} abstract sealed static class AbstractValueLayout & ValueLayout> extends AbstractLayout { @@ -70,15 +71,11 @@ public final class ValueLayouts { @Stable private VarHandle handle; - AbstractValueLayout(Class carrier, ByteOrder order, long bitSize) { - this(carrier, order, bitSize, bitSize, Optional.empty()); - } - AbstractValueLayout(Class carrier, ByteOrder order, long bitSize, long bitAlignment, Optional name) { super(bitSize, bitAlignment, name); this.carrier = carrier; this.order = order; - checkCarrierSize(carrier, bitSize); + assertCarrierSize(carrier, bitSize); } /** @@ -95,11 +92,14 @@ public final class ValueLayouts { * @param order the desired byte order. * @return a value layout with the given byte order. */ - abstract V withOrder(ByteOrder order); + public final V withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return dup(order, bitAlignment(), name()); + } @Override - public final String toString() { - char descriptor = carrier == MemorySegment.class ? 'A' : carrier.descriptorString().charAt(0); + public String toString() { + char descriptor = carrier.descriptorString().charAt(0); if (order == ByteOrder.LITTLE_ENDIAN) { descriptor = Character.toLowerCase(descriptor); } @@ -108,15 +108,11 @@ public final class ValueLayouts { @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!super.equals(other)) { - return false; - } - return other instanceof AbstractValueLayout otherValue && - carrier.equals(otherValue.carrier) && - order.equals(otherValue.order); + return this == other || + other instanceof AbstractValueLayout otherValue && + super.equals(other) && + carrier.equals(otherValue.carrier) && + order.equals(otherValue.order); } public final VarHandle arrayElementVarHandle(int... shape) { @@ -147,24 +143,24 @@ public final class ValueLayouts { } @Override - abstract V dup(long bitAlignment, Optional name); + final V dup(long bitAlignment, Optional name) { + return dup(order(), bitAlignment, name); + } - static void checkCarrierSize(Class carrier, long size) { - if (!isValidCarrier(carrier)) { - throw new IllegalArgumentException("Invalid carrier: " + carrier.getName()); - } - if (carrier == MemorySegment.class && size != ADDRESS_SIZE_BITS) { - throw new IllegalArgumentException("Address size mismatch: " + ADDRESS_SIZE_BITS + " != " + size); - } - if (carrier.isPrimitive()) { - int expectedSize = carrier == boolean.class ? 8 : Wrapper.forPrimitiveType(carrier).bitWidth(); - if (size != expectedSize) { - throw new IllegalArgumentException("Carrier size mismatch: " + carrier.getName() + " != " + size); - } - } + abstract V dup(ByteOrder order, long bitAlignment, Optional name); + + static void assertCarrierSize(Class carrier, long bitSize) { + assert isValidCarrier(carrier); + assert carrier != MemorySegment.class + // MemorySegment bitSize must always equal ADDRESS_SIZE_BITS + || bitSize == ADDRESS_SIZE_BITS; + assert !carrier.isPrimitive() || + // Primitive class bitSize must always correspond + bitSize == (carrier == boolean.class ? 8 : Wrapper.forPrimitiveType(carrier).bitWidth()); } static boolean isValidCarrier(Class carrier) { + // void.class is not valid return carrier == boolean.class || carrier == byte.class || carrier == short.class @@ -176,7 +172,6 @@ public final class ValueLayouts { || carrier == MemorySegment.class; } - @ForceInline public final VarHandle accessHandle() { if (handle == null) { @@ -194,264 +189,236 @@ public final class ValueLayouts { public static final class OfBooleanImpl extends AbstractValueLayout implements ValueLayout.OfBoolean { - private OfBooleanImpl(ByteOrder order) { - super(boolean.class, order, 8); - } - private OfBooleanImpl(ByteOrder order, long bitAlignment, Optional name) { - super(boolean.class, order, 8, bitAlignment, name); + super(boolean.class, order, Byte.SIZE, bitAlignment, name); } @Override - OfBooleanImpl dup(long bitAlignment, Optional name) { - return new OfBooleanImpl(order(), bitAlignment, name); - } - - @Override - public OfBooleanImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfBooleanImpl(order, bitAlignment(), name()); + OfBooleanImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfBooleanImpl(order, bitAlignment, name); } public static OfBoolean of(ByteOrder order) { - return new OfBooleanImpl(order); + return new OfBooleanImpl(order, Byte.SIZE, Optional.empty()); } } public static final class OfByteImpl extends AbstractValueLayout implements ValueLayout.OfByte { - private OfByteImpl(ByteOrder order) { - super(byte.class, order, 8); - } - private OfByteImpl(ByteOrder order, long bitAlignment, Optional name) { - super(byte.class, order, 8, bitAlignment, name); + super(byte.class, order, Byte.SIZE, bitAlignment, name); } @Override - OfByteImpl dup(long bitAlignment, Optional name) { - return new OfByteImpl(order(), bitAlignment, name); - } - - @Override - public OfByteImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfByteImpl(order, bitAlignment(), name()); + OfByteImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfByteImpl(order, bitAlignment, name); } public static OfByte of(ByteOrder order) { - return new OfByteImpl(order); + return new OfByteImpl(order, Byte.SIZE, Optional.empty()); } } public static final class OfCharImpl extends AbstractValueLayout implements ValueLayout.OfChar { - private OfCharImpl(ByteOrder order) { - super(char.class, order, 16); - } - private OfCharImpl(ByteOrder order, long bitAlignment, Optional name) { - super(char.class, order, 16, bitAlignment, name); + super(char.class, order, Character.SIZE, bitAlignment, name); } @Override - OfCharImpl dup(long bitAlignment, Optional name) { - return new OfCharImpl(order(), bitAlignment, name); - } - - @Override - public OfCharImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfCharImpl(order, bitAlignment(), name()); + OfCharImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfCharImpl(order, bitAlignment, name); } public static OfChar of(ByteOrder order) { - return new OfCharImpl(order); + return new OfCharImpl(order, Character.SIZE, Optional.empty()); } } public static final class OfShortImpl extends AbstractValueLayout implements ValueLayout.OfShort { - private OfShortImpl(ByteOrder order) { - super(short.class, order, 16); - } - private OfShortImpl(ByteOrder order, long bitAlignment, Optional name) { - super(short.class, order, 16, bitAlignment, name); + super(short.class, order, Short.SIZE, bitAlignment, name); } @Override - OfShortImpl dup(long bitAlignment, Optional name) { - return new OfShortImpl(order(), bitAlignment, name); - } - - @Override - public OfShortImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfShortImpl(order, bitAlignment(), name()); + OfShortImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfShortImpl(order, bitAlignment, name); } public static OfShort of(ByteOrder order) { - return new OfShortImpl(order); + return new OfShortImpl(order, Short.SIZE, Optional.empty()); } } public static final class OfIntImpl extends AbstractValueLayout implements ValueLayout.OfInt { - private OfIntImpl(ByteOrder order) { - super(int.class, order, 32); - } - private OfIntImpl(ByteOrder order, long bitAlignment, Optional name) { - super(int.class, order, 32, bitAlignment, name); + super(int.class, order, Integer.SIZE, bitAlignment, name); } @Override - OfIntImpl dup(long bitAlignment, Optional name) { - return new OfIntImpl(order(), bitAlignment, name); - } - - @Override - public OfIntImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfIntImpl(order, bitAlignment(), name()); + OfIntImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfIntImpl(order, bitAlignment, name); } public static OfInt of(ByteOrder order) { - return new OfIntImpl(order); + return new OfIntImpl(order, Integer.SIZE, Optional.empty()); } } public static final class OfFloatImpl extends AbstractValueLayout implements ValueLayout.OfFloat { - private OfFloatImpl(ByteOrder order) { - super(float.class, order, 32); - } - private OfFloatImpl(ByteOrder order, long bitAlignment, Optional name) { - super(float.class, order, 32, bitAlignment, name); + super(float.class, order, Float.SIZE, bitAlignment, name); } @Override - OfFloatImpl dup(long bitAlignment, Optional name) { - return new OfFloatImpl(order(), bitAlignment, name); - } - - @Override - public OfFloatImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfFloatImpl(order, bitAlignment(), name()); + OfFloatImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfFloatImpl(order, bitAlignment, name); } public static OfFloat of(ByteOrder order) { - return new OfFloatImpl(order); + return new OfFloatImpl(order, Float.SIZE, Optional.empty()); } } public static final class OfLongImpl extends AbstractValueLayout implements ValueLayout.OfLong { - private OfLongImpl(ByteOrder order) { - super(long.class, order, 64); - } - private OfLongImpl(ByteOrder order, long bitAlignment, Optional name) { - super(long.class, order, 64, bitAlignment, name); + super(long.class, order, Long.SIZE, bitAlignment, name); } @Override - OfLongImpl dup(long bitAlignment, Optional name) { - return new OfLongImpl(order(), bitAlignment, name); - } - - @Override - public OfLongImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfLongImpl(order, bitAlignment(), name()); + OfLongImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfLongImpl(order, bitAlignment, name); } public static OfLong of(ByteOrder order) { - return new OfLongImpl(order); + return new OfLongImpl(order, Long.SIZE, Optional.empty()); } } public static final class OfDoubleImpl extends AbstractValueLayout implements ValueLayout.OfDouble { - private OfDoubleImpl(ByteOrder order) { - super(double.class, order, 64); - } - private OfDoubleImpl(ByteOrder order, long bitAlignment, Optional name) { - super(double.class, order, 64, bitAlignment, name); + super(double.class, order, Double.SIZE, bitAlignment, name); } @Override - OfDoubleImpl dup(long bitAlignment, Optional name) { - return new OfDoubleImpl(order(), bitAlignment, name); - } - - @Override - public OfDoubleImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfDoubleImpl(order, bitAlignment(), name()); + OfDoubleImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfDoubleImpl(order, bitAlignment, name); } public static OfDouble of(ByteOrder order) { - return new OfDoubleImpl(order); + return new OfDoubleImpl(order, Double.SIZE, Optional.empty()); } } - public static final class OfAddressImpl extends AbstractValueLayout implements ValueLayout.OfAddress { + public static final class OfAddressImpl extends AbstractValueLayout implements AddressLayout { - private final boolean isUnbounded; + private final MemoryLayout targetLayout; - private OfAddressImpl(ByteOrder order) { - super(MemorySegment.class, order, ADDRESS_SIZE_BITS); - this.isUnbounded = false; // safe - } - - private OfAddressImpl(ByteOrder order, long size, long bitAlignment, boolean isUnbounded, Optional name) { - super(MemorySegment.class, order, size, bitAlignment, name); - this.isUnbounded = isUnbounded; + private OfAddressImpl(ByteOrder order, long bitSize, long bitAlignment, MemoryLayout targetLayout, Optional name) { + super(MemorySegment.class, order, bitSize, bitAlignment, name); + this.targetLayout = targetLayout; } @Override - OfAddressImpl dup(long alignment, Optional name) { - return new OfAddressImpl(order(), bitSize(), alignment, isUnbounded, name); - } - - @Override - public OfAddressImpl withOrder(ByteOrder order) { - Objects.requireNonNull(order); - return new OfAddressImpl(order, bitSize(), bitAlignment(), isUnbounded, name()); + OfAddressImpl dup(ByteOrder order, long bitAlignment, Optional name) { + return new OfAddressImpl(order, bitSize(), bitAlignment,targetLayout, name); } @Override public boolean equals(Object other) { return super.equals(other) && - ((OfAddressImpl) other).isUnbounded == this.isUnbounded; + Objects.equals(((OfAddressImpl)other).targetLayout, this.targetLayout); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), isUnbounded); + return Objects.hash(super.hashCode(), targetLayout); } @Override @CallerSensitive - public OfAddress asUnbounded() { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), OfAddress.class, "asUnbounded"); - return new OfAddressImpl(order(), bitSize(), bitAlignment(), true, name()); + public AddressLayout withTargetLayout(MemoryLayout layout) { + Reflection.ensureNativeAccess(Reflection.getCallerClass(), AddressLayout.class, "withTargetLayout"); + Objects.requireNonNull(layout); + return new OfAddressImpl(order(), bitSize(), bitAlignment(), layout, name()); } @Override - public boolean isUnbounded() { - return isUnbounded; + public AddressLayout withoutTargetLayout() { + return new OfAddressImpl(order(), bitSize(), bitAlignment(), null, name()); } - public static OfAddress of(ByteOrder order) { - return new OfAddressImpl(order); + @Override + public Optional targetLayout() { + return Optional.ofNullable(targetLayout); + } + + public static AddressLayout of(ByteOrder order) { + return new OfAddressImpl(order, ADDRESS_SIZE_BITS, ADDRESS_SIZE_BITS, null, Optional.empty()); + } + + @Override + public String toString() { + char descriptor = 'A'; + if (order() == ByteOrder.LITTLE_ENDIAN) { + descriptor = Character.toLowerCase(descriptor); + } + String str = decorateLayoutString(String.format("%s%d", descriptor, bitSize())); + if (targetLayout != null) { + str += ":" + targetLayout; + } + return str; } } + /** + * Creates a value layout of given Java carrier and byte order. The type of resulting value layout is determined + * by the carrier provided: + *

        + *
      • {@link ValueLayout.OfBoolean}, for {@code boolean.class}
      • + *
      • {@link ValueLayout.OfByte}, for {@code byte.class}
      • + *
      • {@link ValueLayout.OfShort}, for {@code short.class}
      • + *
      • {@link ValueLayout.OfChar}, for {@code char.class}
      • + *
      • {@link ValueLayout.OfInt}, for {@code int.class}
      • + *
      • {@link ValueLayout.OfFloat}, for {@code float.class}
      • + *
      • {@link ValueLayout.OfLong}, for {@code long.class}
      • + *
      • {@link ValueLayout.OfDouble}, for {@code double.class}
      • + *
      • {@link ValueLayout.OfAddress}, for {@code MemorySegment.class}
      • + *
      + * @param carrier the value layout carrier. + * @param order the value layout's byte order. + * @return a value layout with the given Java carrier and byte-order. + * @throws IllegalArgumentException if the carrier type is not supported. + */ + public static ValueLayout valueLayout(Class carrier, ByteOrder order) { + Objects.requireNonNull(carrier); + Objects.requireNonNull(order); + if (carrier == boolean.class) { + return ValueLayouts.OfBooleanImpl.of(order); + } else if (carrier == char.class) { + return ValueLayouts.OfCharImpl.of(order); + } else if (carrier == byte.class) { + return ValueLayouts.OfByteImpl.of(order); + } else if (carrier == short.class) { + return ValueLayouts.OfShortImpl.of(order); + } else if (carrier == int.class) { + return ValueLayouts.OfIntImpl.of(order); + } else if (carrier == float.class) { + return ValueLayouts.OfFloatImpl.of(order); + } else if (carrier == long.class) { + return ValueLayouts.OfLongImpl.of(order); + } else if (carrier == double.class) { + return ValueLayouts.OfDoubleImpl.of(order); + } else if (carrier == MemorySegment.class) { + return ValueLayouts.OfAddressImpl.of(order); + } else { + throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName()); + } + } } diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 84d3cce737b..ccd62c1d744 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -70,7 +70,7 @@ public @interface PreviewFeature { RECORD_PATTERNS, // not used VIRTUAL_THREADS, - @JEP(number=434, title="Foreign Function & Memory API", status="Second Preview") + @JEP(number=442, title="Foreign Function & Memory API", status="Third Preview") FOREIGN, /** * A key for testing. diff --git a/src/java.base/share/classes/jdk/internal/vm/ForeignLinkerSupport.java b/src/java.base/share/classes/jdk/internal/vm/ForeignLinkerSupport.java new file mode 100644 index 00000000000..cf8f17389fb --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/vm/ForeignLinkerSupport.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, 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; + +/** + * Defines a static method to test if the VM has support for the foreign java.lang.foreign.Linker. + */ +public final class ForeignLinkerSupport { + private static final boolean SUPPORTED = isSupported0(); + + private ForeignLinkerSupport() { + } + + /** + * Return true if the VM has support for the foreign Linker. + */ + public static boolean isSupported() { + return SUPPORTED; + } + + private static native boolean isSupported0(); +} diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java index 72fb1824323..82909d9cf66 100644 --- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java @@ -30,7 +30,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.ref.Cleaner.Cleanable; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; @@ -1206,12 +1206,12 @@ public class FileChannelImpl @Override public MemorySegment map(MapMode mode, long offset, long size, - SegmentScope session) + Arena arena) throws IOException { Objects.requireNonNull(mode,"Mode is null"); - Objects.requireNonNull(session, "Session is null"); - MemorySessionImpl sessionImpl = (MemorySessionImpl) session; + Objects.requireNonNull(arena, "Arena is null"); + MemorySessionImpl sessionImpl = MemorySessionImpl.toMemorySession(arena); sessionImpl.checkValidState(); if (offset < 0) throw new IllegalArgumentException("Requested bytes offset must be >= 0."); @@ -1228,7 +1228,7 @@ public class FileChannelImpl if (unmapper != null) { AbstractMemorySegmentImpl segment = new MappedMemorySegmentImpl(unmapper.address(), unmapper, size, - readOnly, session); + readOnly, sessionImpl); MemorySessionImpl.ResourceList.ResourceCleanup resource = new MemorySessionImpl.ResourceList.ResourceCleanup() { @Override @@ -1239,7 +1239,7 @@ public class FileChannelImpl sessionImpl.addOrCleanupIfFail(resource); return segment; } else { - return new MappedMemorySegmentImpl.EmptyMappedMemorySegmentImpl(readOnly, sessionImpl); + return new MappedMemorySegmentImpl(0, null, 0, readOnly, sessionImpl); } } diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c new file mode 100644 index 00000000000..d2058ce0899 --- /dev/null +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include "jdk_internal_foreign_abi_fallback_LibFallback.h" + +#include + +#include +#ifdef _WIN64 +#include +#include +#endif + +#include "jlong.h" + +static JavaVM* VM; +static jclass LibFallback_class; +static jmethodID LibFallback_doUpcall_ID; +static const char* LibFallback_doUpcall_sig = "(JJLjava/lang/invoke/MethodHandle;)V"; + +JNIEXPORT void JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_init(JNIEnv* env, jclass cls) { + (*env)->GetJavaVM(env, &VM); + LibFallback_class = (*env)->FindClass(env, "jdk/internal/foreign/abi/fallback/LibFallback"); + LibFallback_doUpcall_ID = (*env)->GetStaticMethodID(env, + LibFallback_class, "doUpcall", LibFallback_doUpcall_sig); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_sizeofCif(JNIEnv* env, jclass cls) { + return sizeof(ffi_cif); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1prep_1cif(JNIEnv* env, jclass cls, jlong cif, jint abi, jint nargs, jlong rtype, jlong atypes) { + return ffi_prep_cif(jlong_to_ptr(cif), (ffi_abi) abi, (unsigned int) nargs, jlong_to_ptr(rtype), jlong_to_ptr(atypes)); +} +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1get_1struct_1offsets(JNIEnv* env, jclass cls, jint abi, jlong type, jlong offsets) { + return ffi_get_struct_offsets((ffi_abi) abi, jlong_to_ptr(type), jlong_to_ptr(offsets)); +} + +static void do_capture_state(int32_t* value_ptr, int captured_state_mask) { + // keep in synch with jdk.internal.foreign.abi.CapturableState + enum PreservableValues { + NONE = 0, + GET_LAST_ERROR = 1, + WSA_GET_LAST_ERROR = 1 << 1, + ERRNO = 1 << 2 + }; +#ifdef _WIN64 + if (captured_state_mask & GET_LAST_ERROR) { + *value_ptr = GetLastError(); + } + value_ptr++; + if (captured_state_mask & WSA_GET_LAST_ERROR) { + *value_ptr = WSAGetLastError(); + } + value_ptr++; +#endif + if (captured_state_mask & ERRNO) { + *value_ptr = errno; + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_doDowncall(JNIEnv* env, jclass cls, jlong cif, jlong fn, jlong rvalue, jlong avalues, jlong jcaptured_state, jint captured_state_mask) { + ffi_call(jlong_to_ptr(cif), jlong_to_ptr(fn), jlong_to_ptr(rvalue), jlong_to_ptr(avalues)); + + if (captured_state_mask != 0) { + int32_t* captured_state = jlong_to_ptr(jcaptured_state); + do_capture_state(captured_state, captured_state_mask); + } +} + +static void do_upcall(ffi_cif* cif, void* ret, void** args, void* user_data) { + // attach thread + JNIEnv* env; + jint result = (*VM)->AttachCurrentThreadAsDaemon(VM, (void**) &env, NULL); + + // call into doUpcall in LibFallback + jobject upcall_data = (jobject) user_data; + (*env)->CallStaticVoidMethod(env, LibFallback_class, LibFallback_doUpcall_ID, + ptr_to_jlong(ret), ptr_to_jlong(args), upcall_data); + + // always detach for now + (*VM)->DetachCurrentThread(VM); +} + +static void free_closure(JNIEnv* env, void* closure, jobject upcall_data) { + ffi_closure_free(closure); + (*env)->DeleteGlobalRef(env, upcall_data); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_createClosure(JNIEnv* env, jclass cls, jlong cif, jobject upcall_data, jlongArray jptrs) { + void* code; + void* closure = ffi_closure_alloc(sizeof(ffi_closure), &code); + + jobject global_upcall_data = (*env)->NewGlobalRef(env, upcall_data); + + ffi_status status = ffi_prep_closure_loc(closure, jlong_to_ptr(cif), &do_upcall, (void*) global_upcall_data, code); + + if (status != FFI_OK) { + free_closure(env,closure, global_upcall_data); + return status; + } + + jlong* ptrs = (*env)->GetLongArrayElements(env, jptrs, NULL); + ptrs[0] = ptr_to_jlong(closure); + ptrs[1] = ptr_to_jlong(code); + ptrs[2] = ptr_to_jlong(global_upcall_data); + (*env)->ReleaseLongArrayElements(env, jptrs, ptrs, JNI_COMMIT); + + return status; +} + +JNIEXPORT void JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_freeClosure(JNIEnv* env, jclass cls, jlong closure, jlong upcall_data) { + free_closure(env, jlong_to_ptr(closure), jlong_to_ptr(upcall_data)); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1default_1abi(JNIEnv* env, jclass cls) { + return (jint) FFI_DEFAULT_ABI; +} + +JNIEXPORT jshort JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1struct(JNIEnv* env, jclass cls) { + return (jshort) FFI_TYPE_STRUCT; +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1void(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_void); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint8(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint8); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint8(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint8); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint16(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint16); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint16(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint16); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint32(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint32); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint32(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint32); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint64(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint64); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint64(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint64); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1float(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_float); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1double(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_double); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1pointer(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_pointer); +} diff --git a/src/java.base/share/native/libjava/ForeignLinkerSupport.c b/src/java.base/share/native/libjava/ForeignLinkerSupport.c new file mode 100644 index 00000000000..9d989c09d3b --- /dev/null +++ b/src/java.base/share/native/libjava/ForeignLinkerSupport.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include "jni.h" +#include "jvm.h" + +#include "jdk_internal_vm_ForeignLinkerSupport.h" + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_vm_ForeignLinkerSupport_isSupported0(JNIEnv *env, jclass cls) { + return JVM_IsForeignLinkerSupported(); +} diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 035f855a728..5631ea6237c 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -361,6 +361,7 @@ jdk_svc = \ jdk_foreign = \ java/foreign \ + jdk/internal/reflect/CallerSensitive/CheckCSMs.java \ -java/foreign/TestMatrix.java jdk_vector = \ diff --git a/test/jdk/java/foreign/LibraryLookupTest.java b/test/jdk/java/foreign/LibraryLookupTest.java index 8a9c944485c..322d2f02113 100644 --- a/test/jdk/java/foreign/LibraryLookupTest.java +++ b/test/jdk/java/foreign/LibraryLookupTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -23,12 +23,8 @@ import org.testng.annotations.Test; +import java.lang.foreign.*; import java.lang.foreign.Arena; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.nio.file.Path; import java.util.concurrent.ExecutorService; @@ -51,12 +47,12 @@ public class LibraryLookupTest { @Test void testLoadLibraryConfined() { - try (Arena arena0 = Arena.openConfined()) { - callFunc(loadLibrary(arena0.scope())); - try (Arena arena1 = Arena.openConfined()) { - callFunc(loadLibrary(arena1.scope())); - try (Arena arena2 = Arena.openConfined()) { - callFunc(loadLibrary(arena2.scope())); + try (Arena arena0 = Arena.ofConfined()) { + callFunc(loadLibrary(arena0)); + try (Arena arena1 = Arena.ofConfined()) { + callFunc(loadLibrary(arena1)); + try (Arena arena2 = Arena.ofConfined()) { + callFunc(loadLibrary(arena2)); } } } @@ -65,16 +61,16 @@ public class LibraryLookupTest { @Test(expectedExceptions = IllegalStateException.class) void testLoadLibraryConfinedClosed() { MemorySegment addr; - try (Arena arena = Arena.openConfined()) { - addr = loadLibrary(arena.scope()); + try (Arena arena = Arena.ofConfined()) { + addr = loadLibrary(arena); } callFunc(addr); } - private static MemorySegment loadLibrary(SegmentScope session) { + private static MemorySegment loadLibrary(Arena session) { SymbolLookup lib = SymbolLookup.libraryLookup(LIB_PATH, session); MemorySegment addr = lib.find("inc").get(); - assertEquals(addr.scope(), session); + assertEquals(addr.scope(), session.scope()); return addr; } @@ -94,12 +90,12 @@ public class LibraryLookupTest { @Test(expectedExceptions = IllegalArgumentException.class) void testBadLibraryLookupName() { - SymbolLookup.libraryLookup("nonExistent", SegmentScope.global()); + SymbolLookup.libraryLookup("nonExistent", Arena.global()); } @Test(expectedExceptions = IllegalArgumentException.class) void testBadLibraryLookupPath() { - SymbolLookup.libraryLookup(Path.of("nonExistent"), SegmentScope.global()); + SymbolLookup.libraryLookup(Path.of("nonExistent"), Arena.global()); } @Test @@ -116,8 +112,8 @@ public class LibraryLookupTest { @Override public void run() { for (int i = 0 ; i < ITERATIONS ; i++) { - try (Arena arena = Arena.openConfined()) { - callFunc(loadLibrary(arena.scope())); + try (Arena arena = Arena.ofConfined()) { + callFunc(loadLibrary(arena)); } } } @@ -125,8 +121,8 @@ public class LibraryLookupTest { @Test void testLoadLibrarySharedClosed() throws Throwable { - Arena arena = Arena.openShared(); - MemorySegment addr = loadLibrary(arena.scope()); + Arena arena = Arena.ofShared(); + MemorySegment addr = loadLibrary(arena); ExecutorService accessExecutor = Executors.newCachedThreadPool(); for (int i = 0; i < NUM_ACCESSORS ; i++) { accessExecutor.execute(new LibraryAccess(addr)); diff --git a/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java b/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java index 205ce9c7083..de4c49d03b6 100644 --- a/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java +++ b/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -80,7 +80,7 @@ public class MemoryLayoutPrincipalTotalityTest { case SequenceLayout sl -> 0; // leaf case StructLayout sl -> 0; // leaf case UnionLayout ul -> 0; // leaf - case OfAddress oa -> 0; // leaf + case AddressLayout oa -> 0; // leaf case OfBoolean ob -> 0; // leaf case OfByte ob -> 0; // leaf case OfChar oc -> 0; // leaf @@ -100,7 +100,7 @@ public class MemoryLayoutPrincipalTotalityTest { case GroupLayout gl -> 0; case PaddingLayout pl -> 0; // leaf case SequenceLayout sl -> 0; // leaf - case OfAddress oa -> 0; // leaf + case AddressLayout oa -> 0; // leaf case OfBoolean ob -> 0; // leaf case OfByte ob -> 0; // leaf case OfChar oc -> 0; // leaf diff --git a/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java b/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java index 31a38813330..35c655af3eb 100644 --- a/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java +++ b/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -41,13 +41,16 @@ public class MemoryLayoutTypeRetentionTest { // withName() et al. should return the same type as the original object. private static final String NAME = "a"; - private static final long BIT_ALIGNMENT = 64; - private static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN; + private static final long BIT_ALIGNMENT = Byte.SIZE; + private static final ByteOrder BYTE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN + ? ByteOrder.LITTLE_ENDIAN + : ByteOrder.BIG_ENDIAN; @Test public void testOfBoolean() { OfBoolean v = JAVA_BOOLEAN .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); @@ -57,6 +60,7 @@ public class MemoryLayoutTypeRetentionTest { public void testOfByte() { OfByte v = JAVA_BYTE .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); @@ -66,6 +70,7 @@ public class MemoryLayoutTypeRetentionTest { public void testOfShort() { OfShort v = JAVA_SHORT .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); @@ -75,6 +80,7 @@ public class MemoryLayoutTypeRetentionTest { public void testOfInt() { OfInt v = JAVA_INT .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); @@ -84,6 +90,7 @@ public class MemoryLayoutTypeRetentionTest { public void testOfChar() { OfChar v = JAVA_CHAR .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); @@ -93,6 +100,7 @@ public class MemoryLayoutTypeRetentionTest { public void testOfLong() { OfLong v = JAVA_LONG .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); @@ -102,6 +110,7 @@ public class MemoryLayoutTypeRetentionTest { public void testOfFloat() { OfFloat v = JAVA_FLOAT .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); @@ -111,51 +120,78 @@ public class MemoryLayoutTypeRetentionTest { public void testOfDouble() { OfDouble v = JAVA_DOUBLE .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); } @Test - public void testOfAddress() { - OfAddress v = ADDRESS + public void testValueLayout() { + ValueLayout v = ((ValueLayout) JAVA_INT) .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME) .withOrder(BYTE_ORDER); check(v); - assertFalse(v.isUnbounded()); - OfAddress v2 = v.asUnbounded(); - assertTrue(v2.isUnbounded()); + } + + @Test + public void testAddressLayout() { + AddressLayout v = ADDRESS + .withBitAlignment(BIT_ALIGNMENT) + .withoutName() + .withName(NAME) + .withoutTargetLayout() + .withOrder(BYTE_ORDER); + check(v); + assertEquals(v.order(), BYTE_ORDER); + + assertFalse(v.targetLayout().isPresent()); + AddressLayout v2 = v.withTargetLayout(JAVA_INT); + assertTrue(v2.targetLayout().isPresent()); + assertEquals(v2.targetLayout().get(), JAVA_INT); + assertTrue(v2.withoutTargetLayout().targetLayout().isEmpty()); } @Test public void testPaddingLayout() { PaddingLayout v = MemoryLayout.paddingLayout(8) .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME); check(v); } @Test public void testGroupLayout() { - GroupLayout v = MemoryLayout.structLayout(JAVA_INT, JAVA_LONG) + GroupLayout v = MemoryLayout.structLayout( + JAVA_INT.withBitAlignment(BIT_ALIGNMENT), + JAVA_LONG.withBitAlignment(BIT_ALIGNMENT)) .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME); check(v); } @Test public void testStructLayout() { - StructLayout v = MemoryLayout.structLayout(JAVA_INT, JAVA_LONG) + StructLayout v = MemoryLayout.structLayout( + JAVA_INT.withBitAlignment(BIT_ALIGNMENT), + JAVA_LONG.withBitAlignment(BIT_ALIGNMENT)) .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME); check(v); } @Test public void testUnionLayout() { - UnionLayout v = MemoryLayout.unionLayout(JAVA_INT, JAVA_LONG) + UnionLayout v = MemoryLayout.unionLayout( + JAVA_INT.withBitAlignment(BIT_ALIGNMENT), + JAVA_LONG.withBitAlignment(BIT_ALIGNMENT)) .withBitAlignment(BIT_ALIGNMENT) + .withoutName() .withName(NAME); check(v); } @@ -166,7 +202,10 @@ public class MemoryLayoutTypeRetentionTest { } public void check(MemoryLayout v) { + // Check name properties assertEquals(v.name().orElseThrow(), NAME); + assertTrue(v.withoutName().name().isEmpty()); + assertEquals(v.bitAlignment(), BIT_ALIGNMENT); assertEquals(v.byteSize() * 8, v.bitSize()); } diff --git a/test/jdk/java/foreign/NativeTestHelper.java b/test/jdk/java/foreign/NativeTestHelper.java index be42434fc4d..0d18097efcf 100644 --- a/test/jdk/java/foreign/NativeTestHelper.java +++ b/test/jdk/java/foreign/NativeTestHelper.java @@ -22,6 +22,7 @@ * */ +import java.lang.foreign.AddressLayout; import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; @@ -30,7 +31,6 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.PaddingLayout; import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.SegmentScope; import java.lang.foreign.SequenceLayout; import java.lang.foreign.StructLayout; import java.lang.foreign.SymbolLookup; @@ -121,7 +121,8 @@ public class NativeTestHelper { /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64) + .withTargetLayout(MemoryLayout.sequenceLayout(C_CHAR)); public static final Linker LINKER = Linker.nativeLinker(); @@ -158,7 +159,7 @@ public class NativeTestHelper { public static MemorySegment upcallStub(Class holder, String name, FunctionDescriptor descriptor) { try { MethodHandle target = MethodHandles.lookup().findStatic(holder, name, descriptor.toMethodType()); - return LINKER.upcallStub(target, descriptor, SegmentScope.auto()); + return LINKER.upcallStub(target, descriptor, Arena.ofAuto()); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -209,7 +210,7 @@ public class NativeTestHelper { elementChecks.add(initField(random, segment, array, array.elementLayout(), sequenceElement(i), allocator)); } return new TestValue(segment, actual -> elementChecks.forEach(check -> check.accept(actual))); - } else if (layout instanceof ValueLayout.OfAddress) { + } else if (layout instanceof AddressLayout) { MemorySegment value = MemorySegment.ofAddress(random.nextLong()); return new TestValue(value, actual -> assertEquals(actual, value)); }else if (layout instanceof ValueLayout.OfByte) { @@ -287,7 +288,7 @@ public class NativeTestHelper { MethodHandle target = MethodHandles.insertArguments(MH_SAVER, 1, fd.argumentLayouts(), capturedArgs, arena, retIdx); target = target.asCollector(Object[].class, fd.argumentLayouts().size()); target = target.asType(fd.toMethodType()); - return LINKER.upcallStub(target, fd, arena.scope()); + return LINKER.upcallStub(target, fd, arena); } private static Object saver(Object[] o, List argLayouts, AtomicReference ref, SegmentAllocator allocator, int retArg) { diff --git a/test/jdk/java/foreign/SafeFunctionAccessTest.java b/test/jdk/java/foreign/SafeFunctionAccessTest.java index 4eaf96617a1..b62d14cc884 100644 --- a/test/jdk/java/foreign/SafeFunctionAccessTest.java +++ b/test/jdk/java/foreign/SafeFunctionAccessTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -34,12 +34,10 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.foreign.VaList; import java.util.stream.Stream; import org.testng.annotations.*; @@ -58,7 +56,7 @@ public class SafeFunctionAccessTest extends NativeTestHelper { @Test(expectedExceptions = IllegalStateException.class) public void testClosedStruct() throws Throwable { MemorySegment segment; - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { segment = arena.allocate(POINT); } assertFalse(segment.scope().isAlive()); @@ -76,7 +74,7 @@ public class SafeFunctionAccessTest extends NativeTestHelper { FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER)); record Allocation(Arena drop, MemorySegment segment) { static Allocation of(MemoryLayout layout) { - Arena arena = Arena.openShared(); + Arena arena = Arena.ofShared(); return new Allocation(arena, arena.allocate(layout)); } } @@ -112,26 +110,12 @@ public class SafeFunctionAccessTest extends NativeTestHelper { } } - @Test(expectedExceptions = IllegalStateException.class) - public void testClosedVaList() throws Throwable { - VaList list; - try (Arena arena = Arena.openConfined()) { - list = VaList.make(b -> b.addVarg(C_INT, 42), arena.scope()); - } - assertFalse(list.segment().scope().isAlive()); - MethodHandle handle = Linker.nativeLinker().downcallHandle( - findNativeOrThrow("addr_func"), - FunctionDescriptor.ofVoid(C_POINTER)); - - handle.invokeExact(list.segment()); - } - @Test(expectedExceptions = IllegalStateException.class) public void testClosedUpcall() throws Throwable { MemorySegment upcall; - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class)); - upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena.scope()); + upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena); } assertFalse(upcall.scope().isAlive()); MethodHandle handle = Linker.nativeLinker().downcallHandle( @@ -143,25 +127,13 @@ public class SafeFunctionAccessTest extends NativeTestHelper { static void dummy() { } - @Test - public void testClosedVaListCallback() throws Throwable { - MethodHandle handle = Linker.nativeLinker().downcallHandle( - findNativeOrThrow("addr_func_cb"), - FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); - - try (Arena arena = Arena.openConfined()) { - VaList list = VaList.make(b -> b.addVarg(C_INT, 42), arena.scope()); - handle.invokeExact(list.segment(), sessionChecker(arena)); - } - } - @Test public void testClosedStructCallback() throws Throwable { MethodHandle handle = Linker.nativeLinker().downcallHandle( findNativeOrThrow("addr_func_cb"), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(POINT); handle.invokeExact(segment, sessionChecker(arena)); } @@ -173,9 +145,9 @@ public class SafeFunctionAccessTest extends NativeTestHelper { findNativeOrThrow("addr_func_cb"), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class)); - MemorySegment upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena.scope()); + MemorySegment upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena); handle.invokeExact(upcall, sessionChecker(arena)); } } @@ -185,7 +157,7 @@ public class SafeFunctionAccessTest extends NativeTestHelper { MethodHandle handle = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "checkSession", MethodType.methodType(void.class, Arena.class)); handle = handle.bindTo(arena); - return Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), SegmentScope.auto()); + return Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), Arena.ofAuto()); } catch (Throwable ex) { throw new AssertionError(ex); } diff --git a/test/jdk/java/foreign/StdLibTest.java b/test/jdk/java/foreign/StdLibTest.java index 0eeea1e0880..aeab749370e 100644 --- a/test/jdk/java/foreign/StdLibTest.java +++ b/test/jdk/java/foreign/StdLibTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -41,7 +41,6 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -135,21 +134,6 @@ public class StdLibTest extends NativeTestHelper { assertEquals(found, expected.length()); } - @Test(dataProvider = "printfArgs") - void test_vprintf(List args) throws Throwable { - String formatArgs = args.stream() - .map(a -> a.format) - .collect(Collectors.joining(",")); - - String formatString = "hello(" + formatArgs + ")\n"; - - String expected = String.format(formatString, args.stream() - .map(a -> a.javaValue).toArray()); - - int found = stdLibHelper.vprintf(formatString, args); - assertEquals(found, expected.length()); - } - static class StdLibHelper { final static MethodHandle strcat = abi.downcallHandle(abi.defaultLookup().find("strcat").get(), @@ -165,12 +149,13 @@ public class StdLibTest extends NativeTestHelper { FunctionDescriptor.of(C_INT, C_POINTER)); final static MethodHandle gmtime = abi.downcallHandle(abi.defaultLookup().find("gmtime").get(), - FunctionDescriptor.of(C_POINTER, C_POINTER)); + FunctionDescriptor.of(C_POINTER.withTargetLayout(Tm.LAYOUT), C_POINTER)); final static MethodHandle qsort = abi.downcallHandle(abi.defaultLookup().find("qsort").get(), FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER)); - final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER); + final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, + C_POINTER.withTargetLayout(C_INT), C_POINTER.withTargetLayout(C_INT)); final static MethodHandle qsortCompar; @@ -195,7 +180,7 @@ public class StdLibTest extends NativeTestHelper { } String strcat(String s1, String s2) throws Throwable { - try (var arena = Arena.openConfined()) { + try (var arena = Arena.ofConfined()) { MemorySegment buf = arena.allocate(s1.length() + s2.length() + 1); buf.setUtf8String(0, s1); MemorySegment other = arena.allocateUtf8String(s2); @@ -204,7 +189,7 @@ public class StdLibTest extends NativeTestHelper { } int strcmp(String s1, String s2) throws Throwable { - try (var arena = Arena.openConfined()) { + try (var arena = Arena.ofConfined()) { MemorySegment ns1 = arena.allocateUtf8String(s1); MemorySegment ns2 = arena.allocateUtf8String(s2); return (int)strcmp.invokeExact(ns1, ns2); @@ -212,21 +197,21 @@ public class StdLibTest extends NativeTestHelper { } int puts(String msg) throws Throwable { - try (var arena = Arena.openConfined()) { + try (var arena = Arena.ofConfined()) { MemorySegment s = arena.allocateUtf8String(msg); return (int)puts.invokeExact(s); } } int strlen(String msg) throws Throwable { - try (var arena = Arena.openConfined()) { + try (var arena = Arena.ofConfined()) { MemorySegment s = arena.allocateUtf8String(msg); return (int)strlen.invokeExact(s); } } Tm gmtime(long arg) throws Throwable { - try (var arena = Arena.openConfined()) { + try (var arena = Arena.ofConfined()) { MemorySegment time = arena.allocate(8); time.set(C_LONG_LONG, 0, arg); return new Tm((MemorySegment)gmtime.invokeExact(time)); @@ -238,10 +223,21 @@ public class StdLibTest extends NativeTestHelper { //Tm pointer should never be freed directly, as it points to shared memory private final MemorySegment base; - static final long SIZE = 56; + static final MemoryLayout LAYOUT = MemoryLayout.structLayout( + C_INT.withName("sec"), + C_INT.withName("min"), + C_INT.withName("hour"), + C_INT.withName("mday"), + C_INT.withName("mon"), + C_INT.withName("year"), + C_INT.withName("wday"), + C_INT.withName("yday"), + C_BOOL.withName("isdst"), + MemoryLayout.paddingLayout(24) + ); Tm(MemorySegment addr) { - this.base = addr.asSlice(0, SIZE); + this.base = addr; } int sec() { @@ -275,11 +271,11 @@ public class StdLibTest extends NativeTestHelper { int[] qsort(int[] arr) throws Throwable { //init native array - try (var arena = Arena.openConfined()) { + try (var arena = Arena.ofConfined()) { MemorySegment nativeArr = arena.allocateArray(C_INT, arr); //call qsort - MemorySegment qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, arena.scope()); + MemorySegment qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, arena); qsort.invokeExact(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub); @@ -298,21 +294,13 @@ public class StdLibTest extends NativeTestHelper { } int printf(String format, List args) throws Throwable { - try (var arena = Arena.openConfined()) { + try (var arena = Arena.ofConfined()) { MemorySegment formatStr = arena.allocateUtf8String(format); return (int)specializedPrintf(args).invokeExact(formatStr, args.stream().map(a -> a.nativeValue(arena)).toArray()); } } - int vprintf(String format, List args) throws Throwable { - try (var arena = Arena.openConfined()) { - MemorySegment formatStr = arena.allocateUtf8String(format); - VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, arena)), arena.scope()); - return (int)vprintf.invokeExact(formatStr, vaList.segment()); - } - } - private MethodHandle specializedPrintf(List args) { //method type MethodType mt = MethodType.methodType(int.class, MemorySegment.class); @@ -384,40 +372,27 @@ public class StdLibTest extends NativeTestHelper { .toArray(Object[][]::new); } - enum PrintfArg implements BiConsumer { + enum PrintfArg { - INTEGRAL(int.class, C_INT, "%d", arena -> 42, 42, VaList.Builder::addVarg), + INTEGRAL(int.class, C_INT, "%d", arena -> 42, 42), STRING(MemorySegment.class, C_POINTER, "%s", arena -> { return arena.allocateUtf8String("str"); - }, "str", VaList.Builder::addVarg), - CHAR(byte.class, C_CHAR, "%c", arena -> (byte) 'h', 'h', (builder, layout, value) -> builder.addVarg(C_INT, (int)value)), - DOUBLE(double.class, C_DOUBLE, "%.4f", arena ->1.2345d, 1.2345d, VaList.Builder::addVarg); + }, "str"), + CHAR(byte.class, C_CHAR, "%c", arena -> (byte) 'h', 'h'), + DOUBLE(double.class, C_DOUBLE, "%.4f", arena ->1.2345d, 1.2345d); final Class carrier; final ValueLayout layout; final String format; final Function nativeValueFactory; final Object javaValue; - @SuppressWarnings("rawtypes") - final VaListBuilderCall builderCall; - PrintfArg(Class carrier, L layout, String format, Function nativeValueFactory, Object javaValue, VaListBuilderCall builderCall) { + PrintfArg(Class carrier, L layout, String format, Function nativeValueFactory, Object javaValue) { this.carrier = carrier; this.layout = layout; this.format = format; this.nativeValueFactory = nativeValueFactory; this.javaValue = javaValue; - this.builderCall = builderCall; - } - - @Override - @SuppressWarnings("unchecked") - public void accept(VaList.Builder builder, Arena arena) { - builderCall.build(builder, layout, nativeValueFactory.apply(arena)); - } - - interface VaListBuilderCall { - void build(VaList.Builder builder, L layout, V value); } public Object nativeValue(Arena arena) { diff --git a/test/jdk/java/foreign/TestAdaptVarHandles.java b/test/jdk/java/foreign/TestAdaptVarHandles.java index de74ed8c6b2..893755f15d4 100644 --- a/test/jdk/java/foreign/TestAdaptVarHandles.java +++ b/test/jdk/java/foreign/TestAdaptVarHandles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -31,10 +31,8 @@ * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAdaptVarHandles */ -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; + import org.testng.annotations.*; import static org.testng.Assert.*; @@ -94,7 +92,8 @@ public class TestAdaptVarHandles { @Test public void testFilterValue() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle = layout.varHandle(); VarHandle i2SHandle = MethodHandles.filterValue(intHandle, S2I, I2S); i2SHandle.set(segment, "1"); @@ -113,7 +112,8 @@ public class TestAdaptVarHandles { @Test public void testFilterValueComposite() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle = layout.varHandle(); MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class); VarHandle i2SHandle = MethodHandles.filterValue(intHandle, CTX_S2I, CTX_I2S); @@ -134,7 +134,8 @@ public class TestAdaptVarHandles { @Test public void testFilterValueLoose() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle = layout.varHandle(); VarHandle i2SHandle = MethodHandles.filterValue(intHandle, O2I, I2O); i2SHandle.set(segment, "1"); @@ -191,8 +192,8 @@ public class TestAdaptVarHandles { public void testBadFilterBoxHandleException() { VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); VarHandle vh = MethodHandles.filterValue(intHandle, S2I, I2S_EX); - try (Arena arena = Arena.openConfined()) { - MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment seg = arena.allocate(ValueLayout.JAVA_INT); vh.set(seg, "42"); String x = (String) vh.get(seg); // should throw } @@ -202,8 +203,8 @@ public class TestAdaptVarHandles { public void testBadFilterUnboxHandleException() { VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); VarHandle vh = MethodHandles.filterValue(intHandle, S2I_EX, I2S); - try (Arena arena = Arena.openConfined()) { - MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment seg = arena.allocate(ValueLayout.JAVA_INT); vh.set(seg, "42"); // should throw } } @@ -211,7 +212,8 @@ public class TestAdaptVarHandles { @Test public void testFilterCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle_longIndex = MethodHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L); intHandle_longIndex.set(segment, "0", 1); int oldValue = (int)intHandle_longIndex.getAndAdd(segment, "0", 42); @@ -254,7 +256,8 @@ public class TestAdaptVarHandles { @Test public void testInsertCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle_longIndex = MethodHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L); intHandle_longIndex.set(1); int oldValue = (int)intHandle_longIndex.getAndAdd(42); @@ -292,7 +295,8 @@ public class TestAdaptVarHandles { @Test public void testPermuteCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle_swap = MethodHandles.permuteCoordinates(intHandleIndexed, List.of(long.class, MemorySegment.class), 1, 0); intHandle_swap.set(0L, segment, 1); @@ -331,7 +335,8 @@ public class TestAdaptVarHandles { @Test public void testCollectCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle_sum = MethodHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS); intHandle_sum.set(segment, -2L, 2L, 1); int oldValue = (int)intHandle_sum.getAndAdd(segment, -2L, 2L, 42); @@ -374,7 +379,8 @@ public class TestAdaptVarHandles { @Test public void testDropCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); VarHandle intHandle_dummy = MethodHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class); intHandle_dummy.set(segment, 1f, "hello", 0L, 1); int oldValue = (int)intHandle_dummy.getAndAdd(segment, 1f, "hello", 0L, 42); diff --git a/test/jdk/java/foreign/TestAddressDereference.java b/test/jdk/java/foreign/TestAddressDereference.java new file mode 100644 index 00000000000..22a5a9abc39 --- /dev/null +++ b/test/jdk/java/foreign/TestAddressDereference.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2023, 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. + * + * 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. + */ + +/* + * @test + * @enablePreview + * @library ../ /test/lib + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64" + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestAddressDereference + */ + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.List; + +import org.testng.annotations.*; + +import static org.testng.Assert.*; + +public class TestAddressDereference extends UpcallTestHelper { + + static final Linker LINKER = Linker.nativeLinker(); + static final MemorySegment GET_ADDR_SYM; + static final MethodHandle GET_ADDR_CB_HANDLE, TEST_ARG_HANDLE; + + static { + System.loadLibrary("AddressDereference"); + GET_ADDR_SYM = SymbolLookup.loaderLookup().find("get_addr").get(); + GET_ADDR_CB_HANDLE = LINKER.downcallHandle( + SymbolLookup.loaderLookup().find("get_addr_cb").get(), + FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); + try { + TEST_ARG_HANDLE = MethodHandles.lookup().findStatic(TestAddressDereference.class, "testArg", + MethodType.methodType(void.class, MemorySegment.class, long.class)); + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } + + @Test(dataProvider = "layoutsAndAlignments") + public void testGetAddress(long alignment, ValueLayout layout) { + boolean badAlign = layout.byteAlignment() > alignment; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(ValueLayout.ADDRESS); + segment.set(ValueLayout.ADDRESS, 0, MemorySegment.ofAddress(alignment)); + MemorySegment deref = segment.get(ValueLayout.ADDRESS.withTargetLayout(layout), 0); + assertFalse(badAlign); + assertEquals(deref.byteSize(), layout.byteSize()); + } catch (IllegalArgumentException ex) { + assertTrue(badAlign); + assertTrue(ex.getMessage().contains("alignment constraint for address")); + } + } + + @Test(dataProvider = "layoutsAndAlignments") + public void testGetAddressIndex(long alignment, ValueLayout layout) { + boolean badAlign = layout.byteAlignment() > alignment; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(ValueLayout.ADDRESS); + segment.set(ValueLayout.ADDRESS, 0, MemorySegment.ofAddress(alignment)); + MemorySegment deref = segment.getAtIndex(ValueLayout.ADDRESS.withTargetLayout(layout), 0); + assertFalse(badAlign); + assertEquals(deref.byteSize(), layout.byteSize()); + } catch (IllegalArgumentException ex) { + assertTrue(badAlign); + assertTrue(ex.getMessage().contains("alignment constraint for address")); + } + } + + @Test(dataProvider = "layoutsAndAlignments") + public void testNativeReturn(long alignment, ValueLayout layout) throws Throwable { + boolean badAlign = layout.byteAlignment() > alignment; + try { + MethodHandle get_addr_handle = LINKER.downcallHandle(GET_ADDR_SYM, + FunctionDescriptor.of(ValueLayout.ADDRESS.withTargetLayout(layout), ValueLayout.ADDRESS)); + MemorySegment deref = (MemorySegment)get_addr_handle.invokeExact(MemorySegment.ofAddress(alignment)); + assertFalse(badAlign); + assertEquals(deref.byteSize(), layout.byteSize()); + } catch (IllegalArgumentException ex) { + assertTrue(badAlign); + assertTrue(ex.getMessage().contains("alignment constraint for address")); + } + } + + @Test(dataProvider = "layoutsAndAlignments") + public void testNativeUpcallArgPos(long alignment, ValueLayout layout) throws Throwable { + boolean badAlign = layout.byteAlignment() > alignment; + if (badAlign) return; // this will crash the JVM (exception occurs when going into the upcall stub) + try (Arena arena = Arena.ofConfined()) { + FunctionDescriptor testDesc = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS.withTargetLayout(layout)); + MethodHandle upcallHandle = MethodHandles.insertArguments(TEST_ARG_HANDLE, 1, layout.byteSize()); + MemorySegment testStub = LINKER.upcallStub(upcallHandle, testDesc, arena); + GET_ADDR_CB_HANDLE.invokeExact(MemorySegment.ofAddress(alignment), testStub); + } + } + + @Test(dataProvider = "layoutsAndAlignments") + public void testNativeUpcallArgNeg(long alignment, ValueLayout layout) throws Throwable { + boolean badAlign = layout.byteAlignment() > alignment; + if (!badAlign) return; + runInNewProcess(UpcallTestRunner.class, true, + new String[] {Long.toString(alignment), layout.toString() }) + .assertStdErrContains("alignment constraint for address"); + } + + public static class UpcallTestRunner { + public static void main(String[] args) throws Throwable { + long alignment = parseAlignment(args[0]); + ValueLayout layout = parseLayout(args[1]); + try (Arena arena = Arena.ofConfined()) { + FunctionDescriptor testDesc = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS.withTargetLayout(layout)); + MethodHandle upcallHandle = MethodHandles.insertArguments(TEST_ARG_HANDLE, 1, layout.byteSize()); + MemorySegment testStub = LINKER.upcallStub(upcallHandle, testDesc, arena); + GET_ADDR_CB_HANDLE.invokeExact(MemorySegment.ofAddress(alignment), testStub); + } + } + + static long parseAlignment(String s) { + return Long.parseLong(s); + } + + static ValueLayout parseLayout(String s) { + return LayoutKind.parse(s).layout; + } + } + + static void testArg(MemorySegment deref, long expectedSize) { + assertEquals(deref.byteSize(), expectedSize); + } + + @DataProvider(name = "layoutsAndAlignments") + static Object[][] layoutsAndAlignments() { + List layoutsAndAlignments = new ArrayList<>(); + for (LayoutKind lk : LayoutKind.values()) { + for (int align : new int[]{ 1, 2, 4, 8 }) { + layoutsAndAlignments.add(new Object[] { align, lk.layout }); + } + } + return layoutsAndAlignments.toArray(Object[][]::new); + } + + enum LayoutKind { + BOOL(ValueLayout.JAVA_BOOLEAN), + CHAR(ValueLayout.JAVA_CHAR), + SHORT(ValueLayout.JAVA_SHORT), + INT(ValueLayout.JAVA_INT), + FLOAT(ValueLayout.JAVA_FLOAT), + LONG(ValueLayout.JAVA_LONG), + DOUBLE(ValueLayout.JAVA_DOUBLE), + ADDRESS(ValueLayout.ADDRESS); + + + final ValueLayout layout; + + LayoutKind(ValueLayout segment) { + this.layout = segment; + } + + static LayoutKind parse(String layoutString) { + return switch (layoutString.charAt(0)) { + case 'A','a' -> ADDRESS; + case 'z','Z' -> BOOL; + case 'c','C' -> CHAR; + case 's','S' -> SHORT; + case 'i','I' -> INT; + case 'f','F' -> FLOAT; + case 'j','J' -> LONG; + case 'd','D' -> DOUBLE; + default -> throw new AssertionError("Invalid layout string: " + layoutString); + }; + } + } +} diff --git a/test/jdk/java/foreign/TestArrays.java b/test/jdk/java/foreign/TestArrays.java index e16dfb0d851..603b79e8e94 100644 --- a/test/jdk/java/foreign/TestArrays.java +++ b/test/jdk/java/foreign/TestArrays.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -28,12 +28,8 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestArrays */ -import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; +import java.lang.foreign.*; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SequenceLayout; import java.lang.invoke.VarHandle; import java.util.function.BiConsumer; @@ -108,7 +104,8 @@ public class TestArrays { @Test(dataProvider = "arrays") public void testArrays(Consumer init, Consumer checker, MemoryLayout layout) { - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); init.accept(segment); assertFalse(segment.isReadOnly()); checker.accept(segment); @@ -119,7 +116,7 @@ public class TestArrays { public void testTooBigForArray(MemoryLayout layout, Function arrayFactory) { MemoryLayout seq = MemoryLayout.sequenceLayout((Integer.MAX_VALUE * layout.byteSize()) + 1, layout); //do not really allocate here, as it's way too much memory - MemorySegment segment = MemorySegment.ofAddress(0, seq.byteSize(), SegmentScope.global()); + MemorySegment segment = MemorySegment.NULL.reinterpret(seq.byteSize()); arrayFactory.apply(segment); } @@ -127,8 +124,10 @@ public class TestArrays { expectedExceptions = IllegalStateException.class) public void testBadSize(MemoryLayout layout, Function arrayFactory) { if (layout.byteSize() == 1) throw new IllegalStateException(); //make it fail - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout.byteSize() + 1, layout.byteSize(), arena.scope()); + try (Arena arena = Arena.ofConfined()) { + long byteSize = layout.byteSize() + 1; + long byteAlignment = layout.byteSize(); + MemorySegment segment = arena.allocate(byteSize, byteAlignment); arrayFactory.apply(segment); } } @@ -136,8 +135,8 @@ public class TestArrays { @Test(dataProvider = "elemLayouts", expectedExceptions = IllegalStateException.class) public void testArrayFromClosedSegment(MemoryLayout layout, Function arrayFactory) { - Arena arena = Arena.openConfined(); - MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope()); + Arena arena = Arena.ofConfined(); + MemorySegment segment = arena.allocate(layout); arena.close(); arrayFactory.apply(segment); } diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index 4f13b9112e6..fdd7e60f95a 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -28,16 +28,11 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestByteBuffer */ -import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; +import java.lang.foreign.*; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SequenceLayout; import java.io.File; import java.io.IOException; -import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -185,8 +180,8 @@ public class TestByteBuffer { @Test public void testOffheap() { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(tuples, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(tuples);; initTuples(segment, tuples.elementCount()); ByteBuffer bb = segment.asByteBuffer(); @@ -230,15 +225,15 @@ public class TestByteBuffer { @Test public void testDefaultAccessModesMappedSegment() throws Throwable { - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena); assertFalse(segment.isReadOnly()); } - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 8L, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 8L, arena); assertTrue(segment.isReadOnly()); } } @@ -249,18 +244,18 @@ public class TestByteBuffer { f.createNewFile(); f.deleteOnExit(); - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { //write to channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, tuples.byteSize(), arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, tuples.byteSize(), arena); initTuples(segment, tuples.elementCount()); segment.force(); } - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { //read from channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena); checkTuples(segment, segment.asByteBuffer(), tuples.elementCount()); } } @@ -271,9 +266,9 @@ public class TestByteBuffer { f.createNewFile(); f.deleteOnExit(); - Arena arena = Arena.openConfined(); + Arena arena = Arena.ofConfined(); try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena); assertTrue(segment.isMapped()); arena.close(); mappedBufferOp.apply(segment); @@ -290,10 +285,10 @@ public class TestByteBuffer { // write one at a time for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) { - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { //write to channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, i, tuples.byteSize(), arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, i, tuples.byteSize(), arena); initTuples(segment, 1); segment.force(); } @@ -301,15 +296,54 @@ public class TestByteBuffer { // check one at a time for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) { - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { //read from channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena); checkTuples(segment, segment.asByteBuffer(), 1); } } } + @Test(dataProvider = "fromArrays") + public void testAsByteBufferFromNonByteArray(MemorySegment segment) { + if (!segment.heapBase().map(a -> a instanceof byte[]).get()) { + // This should not work as the segment is not backed by a byte array + assertThrows(UnsupportedOperationException.class, segment::asByteBuffer); + } + } + + @Test + public void testMappedSegmentAsByteBuffer() throws Throwable { + File f = new File("test4.out"); + assertTrue(f.createNewFile()); + f.deleteOnExit(); + + for (var mapOption : List.of(FileChannel.MapMode.READ_WRITE, FileChannel.MapMode.READ_ONLY, FileChannel.MapMode.PRIVATE)) { + for (var arena : List.of(Arena.ofConfined(), Arena.global())) { + try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { + //write to channel + MemorySegment segment = fileChannel.map(mapOption, 0L, 32L, arena); + segment.force(); + segment.load(); + segment.isLoaded(); + segment.unload(); + ByteBuffer byteBuffer = segment.asByteBuffer(); + assertEquals(byteBuffer.capacity(), segment.byteSize()); + assertEquals(byteBuffer.isReadOnly(), segment.isReadOnly()); + assertTrue(byteBuffer.isDirect()); + } catch (IOException e) { + if (e.getMessage().equals("Function not implemented")) + throw new SkipException(e.getMessage(), e); + } finally { + if (arena.scope() != Arena.global().scope()) { + arena.close(); + } + } + } + } + } + static final long LARGE_SIZE = 3L * 1024L * 1024L * 1024L; // 3GB @Test @@ -322,9 +356,9 @@ public class TestByteBuffer { f.createNewFile(); f.deleteOnExit(); - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, LARGE_SIZE, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, LARGE_SIZE, arena); segment.isLoaded(); segment.load(); segment.isLoaded(); @@ -360,8 +394,8 @@ public class TestByteBuffer { @Test(dataProvider = "bufferOps") public void testScopedBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { Buffer bb; - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(bytes);; bb = bufferFactory.apply(segment.asByteBuffer()); } //outside of session!! @@ -386,8 +420,8 @@ public class TestByteBuffer { @Test(dataProvider = "bufferHandleOps") public void testScopedBufferAndVarHandle(VarHandle bufferHandle) { ByteBuffer bb; - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(bytes);; bb = segment.asByteBuffer(); for (Map.Entry e : varHandleMembers(bb, bufferHandle).entrySet()) { MethodHandle handle = e.getKey().bindTo(bufferHandle) @@ -420,8 +454,8 @@ public class TestByteBuffer { @Test(dataProvider = "bufferOps") public void testDirectBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(bytes);; Buffer bb = bufferFactory.apply(segment.asByteBuffer()); assertTrue(bb.isDirect()); DirectBuffer directBuffer = ((DirectBuffer)bb); @@ -433,8 +467,8 @@ public class TestByteBuffer { @Test(dataProvider="resizeOps") public void testResizeOffheap(Consumer checker, Consumer initializer, SequenceLayout seq) { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(seq);; initializer.accept(segment); checker.accept(segment); } @@ -471,8 +505,8 @@ public class TestByteBuffer { @Test(dataProvider="resizeOps") public void testResizeRoundtripNative(Consumer checker, Consumer initializer, SequenceLayout seq) { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(seq);; initializer.accept(segment); MemorySegment second = MemorySegment.ofBuffer(segment.asByteBuffer()); checker.accept(second); @@ -482,8 +516,8 @@ public class TestByteBuffer { @Test(expectedExceptions = IllegalStateException.class) public void testBufferOnClosedSession() { MemorySegment leaked; - try (Arena arena = Arena.openConfined()) { - leaked = MemorySegment.allocateNative(bytes, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + leaked = arena.allocate(bytes);; } ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok byteBuffer.get(); // should throw @@ -491,7 +525,7 @@ public class TestByteBuffer { @Test(expectedExceptions = IllegalStateException.class) public void testTooBigForByteBuffer() { - MemorySegment segment = MemorySegment.ofAddress(0, Integer.MAX_VALUE + 10L, SegmentScope.auto()); + MemorySegment segment = MemorySegment.NULL.reinterpret(Integer.MAX_VALUE + 10L); segment.asByteBuffer(); } @@ -501,7 +535,7 @@ public class TestByteBuffer { f.createNewFile(); f.deleteOnExit(); try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, -1L, SegmentScope.auto()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, -1L, Arena.ofAuto()); } } @@ -511,7 +545,7 @@ public class TestByteBuffer { f.createNewFile(); f.deleteOnExit(); try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - fileChannel.map(FileChannel.MapMode.READ_WRITE, -1L, 1L, SegmentScope.auto()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, -1L, 1L, Arena.ofAuto()); } } @@ -523,9 +557,9 @@ public class TestByteBuffer { int SIZE = Byte.MAX_VALUE; - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SIZE, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SIZE, arena); for (byte offset = 0; offset < SIZE; offset++) { segment.set(JAVA_BYTE, offset, offset); } @@ -533,9 +567,9 @@ public class TestByteBuffer { } for (int offset = 0 ; offset < SIZE ; offset++) { - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, SIZE - offset, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, SIZE - offset, arena); assertEquals(segment.get(JAVA_BYTE, 0), offset); } } @@ -547,9 +581,9 @@ public class TestByteBuffer { f.createNewFile(); f.deleteOnExit(); //RW - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, arena); assertEquals(segment.byteSize(), 0); assertEquals(segment.isMapped(), true); assertFalse(segment.isReadOnly()); @@ -557,11 +591,14 @@ public class TestByteBuffer { segment.load(); segment.isLoaded(); segment.unload(); + ByteBuffer byteBuffer = segment.asByteBuffer(); + assertEquals(byteBuffer.capacity(), 0); + assertFalse(byteBuffer.isReadOnly()); } //RO - try (Arena arena = Arena.openConfined(); + try (Arena arena = Arena.ofConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 0L, arena.scope()); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 0L, arena); assertEquals(segment.byteSize(), 0); assertEquals(segment.isMapped(), true); assertTrue(segment.isReadOnly()); @@ -569,6 +606,9 @@ public class TestByteBuffer { segment.load(); segment.isLoaded(); segment.unload(); + ByteBuffer byteBuffer = segment.asByteBuffer(); + assertEquals(byteBuffer.capacity(), 0); + assertTrue(byteBuffer.isReadOnly()); } } @@ -576,7 +616,7 @@ public class TestByteBuffer { public void testMapCustomPath() throws IOException { Path path = Path.of(URI.create("jrt:/")); try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, SegmentScope.auto()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto()); } } @@ -584,8 +624,8 @@ public class TestByteBuffer { public void testCopyHeapToNative(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int bytes = (int)seq.byteSize(); - try (Arena arena = Arena.openConfined()) { - MemorySegment nativeArray = MemorySegment.allocateNative(bytes, 1, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment nativeArray = arena.allocate(bytes, 1);; MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]); initializer.accept(heapArray); nativeArray.copyFrom(heapArray); @@ -597,8 +637,8 @@ public class TestByteBuffer { public void testCopyNativeToHeap(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int bytes = (int)seq.byteSize(); - try (Arena arena = Arena.openConfined()) { - MemorySegment nativeArray = MemorySegment.allocateNative(seq, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment nativeArray = arena.allocate(seq);; MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]); initializer.accept(nativeArray); heapArray.copyFrom(nativeArray); @@ -669,18 +709,34 @@ public class TestByteBuffer { @Test public void testRoundTripAccess() { - try (Arena arena = Arena.openConfined()) { - MemorySegment ms = MemorySegment.allocateNative(4, 1, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment ms = arena.allocate(4, 1);; MemorySegment msNoAccess = ms.asReadOnly(); MemorySegment msRoundTrip = MemorySegment.ofBuffer(msNoAccess.asByteBuffer()); + assertEquals(msRoundTrip.scope(), ms.scope()); assertEquals(msNoAccess.isReadOnly(), msRoundTrip.isReadOnly()); } } + @Test(dataProvider = "bufferFactories") + public void testDerivedBufferScopes(Supplier bufferFactory) { + MemorySegment segment = MemorySegment.ofBuffer(bufferFactory.get()); + assertEquals(segment.scope(), segment.scope()); + // one level + assertEquals(segment.asSlice(0).scope(), segment.scope()); + assertEquals(segment.asReadOnly().scope(), segment.scope()); + // two levels + assertEquals(segment.asSlice(0).asReadOnly().scope(), segment.scope()); + assertEquals(segment.asReadOnly().asSlice(0).scope(), segment.scope()); + // check fresh every time + MemorySegment another = MemorySegment.ofBuffer(bufferFactory.get()); + assertNotEquals(segment.scope(), another.scope()); + } + @Test(expectedExceptions = IllegalStateException.class) public void testDeadAccessOnClosedBufferSegment() { - Arena arena = Arena.openConfined(); - MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, arena.scope()); + Arena arena = Arena.ofConfined(); + MemorySegment s1 = arena.allocate(JAVA_INT); MemorySegment s2 = MemorySegment.ofBuffer(s1.asByteBuffer()); // memory freed @@ -695,7 +751,7 @@ public class TestByteBuffer { tmp.deleteOnExit(); try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE) ; Arena arena = arenaSupplier.get()) { - MemorySegment segment = MemorySegment.allocateNative(10, 1, arena.scope());; + MemorySegment segment = arena.allocate(10, 1);; for (int i = 0; i < 10; i++) { segment.set(JAVA_BYTE, i, (byte) i); } @@ -716,7 +772,7 @@ public class TestByteBuffer { tmp.deleteOnExit(); Arena arena = arenaSupplier.get(); try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = MemorySegment.allocateNative(10, arena.scope()); + MemorySegment segment = arena.allocate(10, 1); for (int i = 0; i < 10; i++) { segment.set(JAVA_BYTE, i, (byte) i); } @@ -733,8 +789,8 @@ public class TestByteBuffer { @Test public void buffersAndArraysFromSlices() { - try (Arena arena = Arena.openShared()) { - MemorySegment segment = MemorySegment.allocateNative(16, arena.scope());; + try (Arena arena = Arena.ofShared()) { + MemorySegment segment = arena.allocate(16, 1);; int newSize = 8; var slice = segment.asSlice(4, newSize); @@ -751,8 +807,8 @@ public class TestByteBuffer { @Test public void viewsFromSharedSegment() { - try (Arena arena = Arena.openShared()) { - MemorySegment segment = MemorySegment.allocateNative(16, arena.scope());; + try (Arena arena = Arena.ofShared()) { + MemorySegment segment = arena.allocate(16, 1);; var byteBuffer = segment.asByteBuffer(); byteBuffer.asReadOnlyBuffer(); byteBuffer.slice(0, 8); @@ -762,8 +818,8 @@ public class TestByteBuffer { @DataProvider(name = "segments") public static Object[][] segments() throws Throwable { return new Object[][] { - { (Supplier) () -> MemorySegment.allocateNative(16, SegmentScope.auto()) }, - { (Supplier) () -> MemorySegment.allocateNative(16, Arena.openConfined().scope()) }, + { (Supplier) () -> Arena.ofAuto().allocate(16, 1)}, + { (Supplier) () -> Arena.ofConfined().allocate(16, 1)}, { (Supplier) () -> MemorySegment.ofArray(new byte[16]) } }; } @@ -771,8 +827,8 @@ public class TestByteBuffer { @DataProvider(name = "closeableArenas") public static Object[][] closeableArenas() { return new Object[][] { - { (Supplier) Arena::openConfined }, - { (Supplier) Arena::openShared }, + { (Supplier) Arena::ofConfined}, + { (Supplier) Arena::ofShared}, }; } @@ -969,7 +1025,7 @@ public class TestByteBuffer { BUFFER_FORCE(m -> ((MappedByteBuffer)m.asByteBuffer()).force()); - private Consumer segmentOp; + private final Consumer segmentOp; MappedSegmentOp(Consumer segmentOp) { this.segmentOp = segmentOp; @@ -986,4 +1042,41 @@ public class TestByteBuffer { .map(op -> new Object[] { op }) .toArray(Object[][]::new); } + + @DataProvider(name = "bufferFactories") + public static Object[][] bufferFactories() { + List> l = List.of( + () -> ByteBuffer.allocate(10), + () -> CharBuffer.allocate(10), + () -> ShortBuffer.allocate(10), + () -> IntBuffer.allocate(10), + () -> FloatBuffer.allocate(10), + () -> LongBuffer.allocate(10), + () -> DoubleBuffer.allocate(10), + () -> ByteBuffer.allocateDirect(10), + () -> ByteBuffer.allocateDirect(10).asCharBuffer(), + () -> ByteBuffer.allocateDirect(10).asShortBuffer(), + () -> ByteBuffer.allocateDirect(10).asIntBuffer(), + () -> ByteBuffer.allocateDirect(10).asFloatBuffer(), + () -> ByteBuffer.allocateDirect(10).asLongBuffer(), + () -> ByteBuffer.allocateDirect(10).asDoubleBuffer() + ); + return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); + + } + @DataProvider(name = "fromArrays") + public static Object[][] fromArrays() { + int len = 16; + return Stream.of( + MemorySegment.ofArray(new byte[len]), + MemorySegment.ofArray(new short[len]), + MemorySegment.ofArray(new char[len]), + MemorySegment.ofArray(new int[len]), + MemorySegment.ofArray(new long[len]), + MemorySegment.ofArray(new float[len]), + MemorySegment.ofArray(new double[len]) + ) + .map(s -> new Object[] { s }) + .toArray(Object[][]::new); + } } diff --git a/test/jdk/java/foreign/TestClassLoaderFindNative.java b/test/jdk/java/foreign/TestClassLoaderFindNative.java index 080f9673824..8a3b2d392af 100644 --- a/test/jdk/java/foreign/TestClassLoaderFindNative.java +++ b/test/jdk/java/foreign/TestClassLoaderFindNative.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -29,9 +29,7 @@ */ import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.lang.foreign.SymbolLookup; -import java.lang.foreign.ValueLayout; import org.testng.annotations.Test; import static java.lang.foreign.ValueLayout.JAVA_BYTE; @@ -59,10 +57,7 @@ public class TestClassLoaderFindNative { @Test public void testVariableSymbolLookup() { - MemorySegment segment = MemorySegment.ofAddress( - SymbolLookup.loaderLookup().find("c").get().address(), - ValueLayout.JAVA_INT.byteSize(), - SegmentScope.global()); + MemorySegment segment = SymbolLookup.loaderLookup().find("c").get().reinterpret(1); assertEquals(segment.get(JAVA_BYTE, 0), 42); } } diff --git a/test/jdk/java/foreign/TestDereferencePath.java b/test/jdk/java/foreign/TestDereferencePath.java new file mode 100644 index 00000000000..4e50ffca00f --- /dev/null +++ b/test/jdk/java/foreign/TestDereferencePath.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023, 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. + * + * 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. + * + */ + +/* + * @test + * @enablePreview + * @run testng TestDereferencePath + */ + +import java.lang.foreign.Arena; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemoryLayout.PathElement; +import java.lang.foreign.MemorySegment; + +import java.lang.foreign.ValueLayout; + +import org.testng.annotations.*; + +import java.lang.invoke.VarHandle; +import static org.testng.Assert.*; + +public class TestDereferencePath { + + static final MemoryLayout C = MemoryLayout.structLayout( + ValueLayout.JAVA_INT.withName("x") + ); + + static final MemoryLayout B = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("c") + .withTargetLayout(C) + ); + + static final MemoryLayout A = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("b") + .withTargetLayout(B) + ); + + static final VarHandle abcx = A.varHandle( + PathElement.groupElement("b"), PathElement.dereferenceElement(), + PathElement.groupElement("c"), PathElement.dereferenceElement(), + PathElement.groupElement("x")); + + @Test + public void testSingle() { + try (Arena arena = Arena.ofConfined()) { + // init structs + MemorySegment a = arena.allocate(A); + MemorySegment b = arena.allocate(B); + MemorySegment c = arena.allocate(C); + // init struct fields + a.set(ValueLayout.ADDRESS, 0, b); + b.set(ValueLayout.ADDRESS, 0, c); + c.set(ValueLayout.JAVA_INT, 0, 42); + // dereference + int val = (int) abcx.get(a); + assertEquals(val, 42); + } + } + + static final MemoryLayout B_MULTI = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("cs") + .withTargetLayout(MemoryLayout.sequenceLayout(2, C)) + ); + + static final MemoryLayout A_MULTI = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("bs") + .withTargetLayout(MemoryLayout.sequenceLayout(2, B_MULTI)) + ); + + static final VarHandle abcx_multi = A_MULTI.varHandle( + PathElement.groupElement("bs"), PathElement.dereferenceElement(), PathElement.sequenceElement(), + PathElement.groupElement("cs"), PathElement.dereferenceElement(), PathElement.sequenceElement(), + PathElement.groupElement("x")); + + @Test + public void testMulti() { + try (Arena arena = Arena.ofConfined()) { + // init structs + MemorySegment a = arena.allocate(A); + MemorySegment b = arena.allocateArray(B, 2); + MemorySegment c = arena.allocateArray(C, 4); + // init struct fields + a.set(ValueLayout.ADDRESS, 0, b); + b.set(ValueLayout.ADDRESS, 0, c); + b.setAtIndex(ValueLayout.ADDRESS, 1, c.asSlice(C.byteSize() * 2)); + c.setAtIndex(ValueLayout.JAVA_INT, 0, 1); + c.setAtIndex(ValueLayout.JAVA_INT, 1, 2); + c.setAtIndex(ValueLayout.JAVA_INT, 2, 3); + c.setAtIndex(ValueLayout.JAVA_INT, 3, 4); + // dereference + int val00 = (int) abcx_multi.get(a, 0, 0); // a->b[0]->c[0] = 1 + assertEquals(val00, 1); + int val10 = (int) abcx_multi.get(a, 1, 0); // a->b[1]->c[0] = 3 + assertEquals(val10, 3); + int val01 = (int) abcx_multi.get(a, 0, 1); // a->b[0]->c[1] = 2 + assertEquals(val01, 2); + int val11 = (int) abcx_multi.get(a, 1, 1); // a->b[1]->c[1] = 4 + assertEquals(val11, 4); + } + } + + @Test(expectedExceptions = IllegalArgumentException.class) + void testBadDerefInSelect() { + A.select(PathElement.groupElement("b"), PathElement.dereferenceElement()); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + void testBadDerefInOffset() { + A.byteOffset(PathElement.groupElement("b"), PathElement.dereferenceElement()); + } + + static final MemoryLayout A_MULTI_NO_TARGET = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("bs") + ); + + @Test(expectedExceptions = IllegalArgumentException.class) + void badDerefAddressNoTarget() { + A_MULTI_NO_TARGET.varHandle(PathElement.groupElement("bs"), PathElement.dereferenceElement()); + } +} diff --git a/test/jdk/java/foreign/TestDowncallScope.java b/test/jdk/java/foreign/TestDowncallScope.java index 15d34df96c1..698e4a6edca 100644 --- a/test/jdk/java/foreign/TestDowncallScope.java +++ b/test/jdk/java/foreign/TestDowncallScope.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -39,11 +39,7 @@ import org.testng.annotations.Test; -import java.lang.foreign.Arena; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.*; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -63,11 +59,11 @@ public class TestDowncallScope extends TestDowncallBase { List> checks = new ArrayList<>(); MemorySegment addr = findNativeOrThrow(fName); FunctionDescriptor descriptor = function(ret, paramTypes, fields); - try (Arena arena = Arena.openShared()) { + try (Arena arena = Arena.ofShared()) { Object[] args = makeArgs(arena, descriptor, checks); boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false); SegmentAllocator allocator = needsScope ? - SegmentAllocator.nativeAllocator(arena.scope()) : + arena : THROWING_ALLOCATOR; Object res = doCall(addr, allocator, descriptor, args); if (ret == CallGeneratorHelper.Ret.NON_VOID) { diff --git a/test/jdk/java/foreign/TestDowncallStack.java b/test/jdk/java/foreign/TestDowncallStack.java index 4d9eb250bbb..6500e691b9b 100644 --- a/test/jdk/java/foreign/TestDowncallStack.java +++ b/test/jdk/java/foreign/TestDowncallStack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -55,7 +55,7 @@ public class TestDowncallStack extends TestDowncallBase { List> checks = new ArrayList<>(); MemorySegment addr = findNativeOrThrow("s" + fName); FunctionDescriptor descriptor = functionStack(ret, paramTypes, fields); - try (Arena arena = Arena.openShared()) { + try (Arena arena = Arena.ofShared()) { Object[] args = makeArgsStack(arena, descriptor, checks); boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false); SegmentAllocator allocator = needsScope ? diff --git a/test/jdk/java/foreign/TestFunctionDescriptor.java b/test/jdk/java/foreign/TestFunctionDescriptor.java index 798bc09c6df..ff75dfef755 100644 --- a/test/jdk/java/foreign/TestFunctionDescriptor.java +++ b/test/jdk/java/foreign/TestFunctionDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -101,6 +101,12 @@ public class TestFunctionDescriptor extends NativeTestHelper { public void testEquals() { FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, C_INT); assertEquals(fd, fd); + FunctionDescriptor fdReturnSomethingElse = FunctionDescriptor.of(C_LONG_LONG, C_INT, C_INT); + FunctionDescriptor fdOtherArguments = FunctionDescriptor.of(C_INT, C_INT); + assertFalse(fd.equals(fdReturnSomethingElse)); + assertFalse(fd.equals(fdOtherArguments)); + assertFalse(fd.equals(null)); + assertFalse(fd.equals("A")); } @Test @@ -121,4 +127,17 @@ public class TestFunctionDescriptor extends NativeTestHelper { MemoryLayout.paddingLayout(32)); fd.toMethodType(); // should throw } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testIllegalInsertArgNegIndex() { + FunctionDescriptor fd = FunctionDescriptor.of(C_INT); + fd.insertArgumentLayouts(-1, C_INT); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testIllegalInsertArgOutOfBounds() { + FunctionDescriptor fd = FunctionDescriptor.of(C_INT); + fd.insertArgumentLayouts(2, C_INT); + } + } diff --git a/test/jdk/java/foreign/TestHandshake.java b/test/jdk/java/foreign/TestHandshake.java index 519739cb1eb..13d9f9608de 100644 --- a/test/jdk/java/foreign/TestHandshake.java +++ b/test/jdk/java/foreign/TestHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -67,14 +67,14 @@ public class TestHandshake { @Test(dataProvider = "accessors") public void testHandshake(String testName, AccessorFactory accessorFactory) throws InterruptedException { for (int it = 0 ; it < ITERATIONS ; it++) { - Arena arena = Arena.openShared(); - MemorySegment segment = MemorySegment.allocateNative(SEGMENT_SIZE, 1, arena.scope()); + Arena arena = Arena.ofShared(); + MemorySegment segment = arena.allocate(SEGMENT_SIZE, 1); System.out.println("ITERATION " + it); ExecutorService accessExecutor = Executors.newCachedThreadPool(); start.set(System.currentTimeMillis()); started.set(false); for (int i = 0; i < NUM_ACCESSORS ; i++) { - accessExecutor.execute(accessorFactory.make(i, segment)); + accessExecutor.execute(accessorFactory.make(i, segment, arena)); } int delay = ThreadLocalRandom.current().nextInt(MAX_DELAY_MILLIS); System.out.println("Starting handshaker with delay set to " + delay + " millis"); @@ -136,7 +136,7 @@ public class TestHandshake { static abstract class AbstractBufferAccessor extends AbstractSegmentAccessor { final ByteBuffer bb; - AbstractBufferAccessor(int id, MemorySegment segment) { + AbstractBufferAccessor(int id, MemorySegment segment, Arena _unused) { super(id, segment); this.bb = segment.asByteBuffer(); } @@ -144,7 +144,7 @@ public class TestHandshake { static class SegmentAccessor extends AbstractSegmentAccessor { - SegmentAccessor(int id, MemorySegment segment) { + SegmentAccessor(int id, MemorySegment segment, Arena _unused) { super(id, segment); } @@ -162,7 +162,7 @@ public class TestHandshake { MemorySegment first, second; - SegmentCopyAccessor(int id, MemorySegment segment) { + SegmentCopyAccessor(int id, MemorySegment segment, Arena _unused) { super(id, segment); long split = segment.byteSize() / 2; first = segment.asSlice(0, split); @@ -177,7 +177,7 @@ public class TestHandshake { static class SegmentFillAccessor extends AbstractSegmentAccessor { - SegmentFillAccessor(int id, MemorySegment segment) { + SegmentFillAccessor(int id, MemorySegment segment, Arena _unused) { super(id, segment); } @@ -191,9 +191,9 @@ public class TestHandshake { final MemorySegment copy; - SegmentMismatchAccessor(int id, MemorySegment segment) { + SegmentMismatchAccessor(int id, MemorySegment segment, Arena arena) { super(id, segment); - this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.scope()); + this.copy = arena.allocate(SEGMENT_SIZE, 1); copy.copyFrom(segment); copy.set(JAVA_BYTE, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42); } @@ -206,8 +206,8 @@ public class TestHandshake { static class BufferAccessor extends AbstractBufferAccessor { - BufferAccessor(int id, MemorySegment segment) { - super(id, segment); + BufferAccessor(int id, MemorySegment segment, Arena _unused) { + super(id, segment, null); } @Override @@ -223,8 +223,8 @@ public class TestHandshake { static VarHandle handle = MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()); - public BufferHandleAccessor(int id, MemorySegment segment) { - super(id, segment); + public BufferHandleAccessor(int id, MemorySegment segment, Arena _unused) { + super(id, segment, null); } @Override @@ -261,7 +261,7 @@ public class TestHandshake { } interface AccessorFactory { - AbstractSegmentAccessor make(int id, MemorySegment segment); + AbstractSegmentAccessor make(int id, MemorySegment segment, Arena arena); } @DataProvider diff --git a/test/jdk/java/foreign/TestHeapAlignment.java b/test/jdk/java/foreign/TestHeapAlignment.java index 68d9cb29576..d0df2a25b19 100644 --- a/test/jdk/java/foreign/TestHeapAlignment.java +++ b/test/jdk/java/foreign/TestHeapAlignment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -29,9 +29,10 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestHeapAlignment */ +import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import java.util.ArrayList; import java.util.List; @@ -90,7 +91,7 @@ public class TestHeapAlignment { static final ValueLayout.OfFloat JAVA_FLOAT_ALIGNED = ValueLayout.JAVA_FLOAT.withBitAlignment(32); static final ValueLayout.OfLong JAVA_LONG_ALIGNED = ValueLayout.JAVA_LONG.withBitAlignment(64); static final ValueLayout.OfDouble JAVA_DOUBLE_ALIGNED = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); - static final ValueLayout.OfAddress ADDRESS_ALIGNED = ValueLayout.ADDRESS.withBitAlignment(ValueLayout.ADDRESS.bitSize()); + static final AddressLayout ADDRESS_ALIGNED = ValueLayout.ADDRESS.withBitAlignment(ValueLayout.ADDRESS.bitSize()); enum SegmentAndAlignment { HEAP_BYTE(MemorySegment.ofArray(new byte[8]), 1), @@ -100,7 +101,7 @@ public class TestHeapAlignment { HEAP_FLOAT(MemorySegment.ofArray(new float[2]), 4), HEAP_LONG(MemorySegment.ofArray(new long[1]), 8), HEAP_DOUBLE(MemorySegment.ofArray(new double[1]), 8), - NATIVE(MemorySegment.allocateNative(8, SegmentScope.auto()), -1); + NATIVE(Arena.ofAuto().allocate(8, 1), -1); final MemorySegment segment; final int align; diff --git a/test/jdk/java/foreign/TestIllegalLink.java b/test/jdk/java/foreign/TestIllegalLink.java index 2a865aeef8e..cc790667cc0 100644 --- a/test/jdk/java/foreign/TestIllegalLink.java +++ b/test/jdk/java/foreign/TestIllegalLink.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -29,10 +29,15 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestIllegalLink */ +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.nio.ByteOrder; import org.testng.annotations.DataProvider; @@ -44,10 +49,11 @@ import static org.testng.Assert.fail; public class TestIllegalLink extends NativeTestHelper { private static final MemorySegment DUMMY_TARGET = MemorySegment.ofAddress(1); + private static final MethodHandle DUMMY_TARGET_MH = MethodHandles.empty(MethodType.methodType(void.class)); private static final Linker ABI = Linker.nativeLinker(); @Test(dataProvider = "types") - public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMessage) { + public void testIllegalLayouts(FunctionDescriptor desc, String expectedExceptionMessage) { try { ABI.downcallHandle(DUMMY_TARGET, desc); fail("Expected IllegalArgumentException was not thrown"); @@ -57,6 +63,42 @@ public class TestIllegalLink extends NativeTestHelper { } } + @Test(dataProvider = "downcallOnlyOptions", + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = ".*Not supported for upcall.*") + public void testIllegalUpcallOptions(Linker.Option downcallOnlyOption) { + ABI.upcallStub(DUMMY_TARGET_MH, FunctionDescriptor.ofVoid(), Arena.ofAuto(), downcallOnlyOption); + } + + @Test(dataProvider = "illegalCaptureState", + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = ".*Unknown name.*") + public void testIllegalCaptureState(String name) { + Linker.Option.captureCallState(name); + } + + // where + + @DataProvider + public static Object[][] illegalCaptureState() { + if (!IS_WINDOWS) { + return new Object[][]{ + { "GetLastError" }, + { "WSAGetLastError" }, + }; + } + return new Object[][]{}; + } + + @DataProvider + public static Object[][] downcallOnlyOptions() { + return new Object[][]{ + { Linker.Option.firstVariadicArg(0) }, + { Linker.Option.captureCallState("errno") }, + { Linker.Option.isTrivial() }, + }; + } + @DataProvider public static Object[][] types() { return new Object[][]{ @@ -85,7 +127,7 @@ public class TestIllegalLink extends NativeTestHelper { "Layout bit alignment must be natural alignment" }, { - FunctionDescriptor.ofVoid(MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()).withBitAlignment(32)), + FunctionDescriptor.ofVoid(ValueLayout.JAVA_CHAR.withBitAlignment(32)), "Layout bit alignment must be natural alignment" }, { diff --git a/test/jdk/java/foreign/TestIntrinsics.java b/test/jdk/java/foreign/TestIntrinsics.java index 4d83aa2d692..b7a239ed7db 100644 --- a/test/jdk/java/foreign/TestIntrinsics.java +++ b/test/jdk/java/foreign/TestIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -26,7 +26,7 @@ * @enablePreview * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64" * @run testng/othervm - * -Djdk.internal.foreign.ProgrammableInvoker.USE_SPEC=true + * -Djdk.internal.foreign.DowncallLinker.USE_SPEC=true * --enable-native-access=ALL-UNNAMED * -Xbatch * TestIntrinsics diff --git a/test/jdk/java/foreign/TestLargeSegmentCopy.java b/test/jdk/java/foreign/TestLargeSegmentCopy.java index f75c2e75ca9..dc80d966abc 100644 --- a/test/jdk/java/foreign/TestLargeSegmentCopy.java +++ b/test/jdk/java/foreign/TestLargeSegmentCopy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -45,8 +45,8 @@ public class TestLargeSegmentCopy { final int longArrayLength = Integer.MAX_VALUE / Long.BYTES + 100; final long[] array = new long[longArrayLength]; - try (var arena = Arena.openConfined()) { - var segment = MemorySegment.allocateNative((long) longArrayLength * Long.BYTES, Long.SIZE, arena.scope()); + try (var arena = Arena.ofConfined()) { + var segment = arena.allocate((long) longArrayLength * Long.BYTES, Long.SIZE); // Should not throw an exception or error MemorySegment.copy(segment, JAVA_LONG, 0, array, 0, longArrayLength); // Should not throw an exception or error diff --git a/test/jdk/java/foreign/TestLayoutPaths.java b/test/jdk/java/foreign/TestLayoutPaths.java index 59663af8200..45692e8d4f0 100644 --- a/test/jdk/java/foreign/TestLayoutPaths.java +++ b/test/jdk/java/foreign/TestLayoutPaths.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -28,20 +28,16 @@ * @run testng TestLayoutPaths */ -import java.lang.foreign.Arena; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; +import java.lang.foreign.*; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SequenceLayout; -import java.lang.foreign.ValueLayout; import org.testng.SkipException; import org.testng.annotations.*; import java.lang.invoke.MethodHandle; import java.util.ArrayList; import java.util.List; +import java.util.function.IntFunction; import static java.lang.foreign.MemoryLayout.PathElement.groupElement; import static java.lang.foreign.MemoryLayout.PathElement.sequenceElement; @@ -98,6 +94,18 @@ public class TestLayoutPaths { g.byteOffset(groupElement("foo")); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testTooBigGroupElementIndex() { + GroupLayout g = MemoryLayout.structLayout(JAVA_INT); + g.byteOffset(groupElement(1)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNegativeGroupElementIndex() { + GroupLayout g = MemoryLayout.structLayout(JAVA_INT); + g.byteOffset(groupElement(-1)); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testBitOutOfBoundsSeqIndex() { SequenceLayout seq = MemoryLayout.sequenceLayout(5, JAVA_INT); @@ -168,63 +176,6 @@ public class TestLayoutPaths { seq.byteOffsetHandle(sequenceElement(0, 1)); // ranges not accepted } - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testBadMultiple() { - GroupLayout g = MemoryLayout.structLayout(MemoryLayout.paddingLayout(3), JAVA_INT.withName("foo")); - g.byteOffset(groupElement("foo")); - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testBadByteOffsetNoMultipleOf8() { - MemoryLayout layout = MemoryLayout.structLayout(MemoryLayout.paddingLayout(7), JAVA_INT.withName("x")); - layout.byteOffset(groupElement("x")); - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testBadByteOffsetHandleNoMultipleOf8() throws Throwable { - MemoryLayout layout = MemoryLayout.structLayout(MemoryLayout.paddingLayout(7), JAVA_INT.withName("x")); - MethodHandle handle = layout.byteOffsetHandle(groupElement("x")); - handle.invoke(); - } - - @Test - public void testBadContainerAlign() { - GroupLayout g = MemoryLayout.structLayout(JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8); - try { - g.bitOffset(groupElement("foo")); - g.byteOffset(groupElement("foo")); - } catch (Throwable ex) { - throw new AssertionError(ex); // should be ok! - } - try { - g.varHandle(groupElement("foo")); //ok - assertTrue(false); //should fail! - } catch (UnsupportedOperationException ex) { - //ok - } catch (Throwable ex) { - throw new AssertionError(ex); //should fail! - } - } - - @Test - public void testBadAlignOffset() { - GroupLayout g = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), JAVA_INT.withBitAlignment(16).withName("foo")); - try { - g.bitOffset(groupElement("foo")); - g.byteOffset(groupElement("foo")); - } catch (Throwable ex) { - throw new AssertionError(ex); // should be ok! - } - try { - g.varHandle(groupElement("foo")); //ok - assertTrue(false); //should fail! - } catch (UnsupportedOperationException ex) { - //ok - } catch (Throwable ex) { - throw new AssertionError(ex); //should fail! - } - } - @Test public void testBadSequencePathInOffset() { SequenceLayout seq = MemoryLayout.sequenceLayout(10, JAVA_INT); @@ -258,60 +209,68 @@ public class TestLayoutPaths { } } - @Test - public void testStructPaths() { + @Test(dataProvider = "groupSelectors") + public void testStructPaths(IntFunction groupSelector) { long[] offsets = { 0, 8, 24, 56 }; GroupLayout g = MemoryLayout.structLayout( - ValueLayout.JAVA_BYTE.withName("1"), - ValueLayout.JAVA_CHAR.withName("2"), - ValueLayout.JAVA_FLOAT.withName("3"), - ValueLayout.JAVA_LONG.withName("4") + ValueLayout.JAVA_BYTE.withName("0"), + ValueLayout.JAVA_CHAR.withBitAlignment(8).withName("1"), + ValueLayout.JAVA_FLOAT.withBitAlignment(8).withName("2"), + ValueLayout.JAVA_LONG.withBitAlignment(8).withName("3") ); // test select - for (int i = 1 ; i <= 4 ; i++) { - MemoryLayout selected = g.select(groupElement(String.valueOf(i))); - assertTrue(selected == g.memberLayouts().get(i - 1)); + for (int i = 0 ; i < 4 ; i++) { + MemoryLayout selected = g.select(groupSelector.apply(i)); + assertTrue(selected == g.memberLayouts().get(i)); } // test offset - for (int i = 1 ; i <= 4 ; i++) { - long bitOffset = g.bitOffset(groupElement(String.valueOf(i))); - assertEquals(offsets[i - 1], bitOffset); - long byteOffset = g.byteOffset(groupElement(String.valueOf(i))); - assertEquals((offsets[i - 1]) >>> 3, byteOffset); + for (int i = 0 ; i < 4 ; i++) { + long bitOffset = g.bitOffset(groupSelector.apply(i)); + assertEquals(offsets[i], bitOffset); + long byteOffset = g.byteOffset(groupSelector.apply(i)); + assertEquals((offsets[i]) >>> 3, byteOffset); } } - @Test - public void testUnionPaths() { + @Test(dataProvider = "groupSelectors") + public void testUnionPaths(IntFunction groupSelector) { long[] offsets = { 0, 0, 0, 0 }; GroupLayout g = MemoryLayout.unionLayout( - ValueLayout.JAVA_BYTE.withName("1"), - ValueLayout.JAVA_CHAR.withName("2"), - ValueLayout.JAVA_FLOAT.withName("3"), - ValueLayout.JAVA_LONG.withName("4") + ValueLayout.JAVA_BYTE.withName("0"), + ValueLayout.JAVA_CHAR.withName("1"), + ValueLayout.JAVA_FLOAT.withName("2"), + ValueLayout.JAVA_LONG.withName("3") ); // test select - for (int i = 1 ; i <= 4 ; i++) { - MemoryLayout selected = g.select(groupElement(String.valueOf(i))); - assertTrue(selected == g.memberLayouts().get(i - 1)); + for (int i = 0 ; i < 4 ; i++) { + MemoryLayout selected = g.select(groupSelector.apply(i)); + assertTrue(selected == g.memberLayouts().get(i)); } // test offset - for (int i = 1 ; i <= 4 ; i++) { - long bitOffset = g.bitOffset(groupElement(String.valueOf(i))); - assertEquals(offsets[i - 1], bitOffset); - long byteOffset = g.byteOffset(groupElement(String.valueOf(i))); - assertEquals((offsets[i - 1]) >>> 3, byteOffset); + for (int i = 0 ; i < 4 ; i++) { + long bitOffset = g.bitOffset(groupSelector.apply(i)); + assertEquals(offsets[i], bitOffset); + long byteOffset = g.byteOffset(groupSelector.apply(i)); + assertEquals((offsets[i]) >>> 3, byteOffset); } } + @DataProvider + public static Object[][] groupSelectors() { + return new Object[][] { + { (IntFunction) PathElement::groupElement }, // by index + { (IntFunction) i -> PathElement.groupElement(String.valueOf(i)) } // by name + }; + } + @Test public void testSequencePaths() { long[] offsets = { 0, 8, 16, 24 }; @@ -364,10 +323,10 @@ public class TestLayoutPaths { (JAVA_INT.bitSize() * 2) * 4 + JAVA_INT.bitSize() }); testCases.add(new Object[] { - MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(5), JAVA_INT.withName("y"))), + MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(32), JAVA_INT.withName("y"))), new PathElement[] { sequenceElement(), groupElement("y") }, new long[] { 4 }, - (JAVA_INT.bitSize() + 5) * 4 + 5 + (JAVA_INT.bitSize() + 32) * 4 + 32 }); testCases.add(new Object[] { MemoryLayout.sequenceLayout(10, JAVA_INT), @@ -433,53 +392,13 @@ public class TestLayoutPaths { MethodHandle sliceHandle = layout.sliceHandle(pathElements); sliceHandle = sliceHandle.asSpreader(long[].class, indexes.length); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(layout); MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, indexes); assertEquals(slice.address() - segment.address(), expectedBitOffset / 8); assertEquals(slice.byteSize(), selected.byteSize()); } } - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testSliceHandleUOEInvalidOffsetEager() throws Throwable { - MemoryLayout layout = MemoryLayout.structLayout( - MemoryLayout.paddingLayout(5), - JAVA_INT.withName("y") // offset not a multiple of 8 - ); - - layout.sliceHandle(groupElement("y")); // should throw - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testSliceHandleUOEInvalidOffsetLate() throws Throwable { - MemoryLayout layout = MemoryLayout.sequenceLayout(3, - MemoryLayout.structLayout( - MemoryLayout.paddingLayout(4), - JAVA_INT.withName("y") // offset not a multiple of 8 - ) - ); - - MethodHandle sliceHandle; - try { - sliceHandle = layout.sliceHandle(sequenceElement(), groupElement("y")); // should work - } catch (UnsupportedOperationException uoe) { - fail("Unexpected exception", uoe); - return; - } - - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope()); - - try { - sliceHandle.invokeExact(segment, 1); // should work - } catch (UnsupportedOperationException uoe) { - fail("Unexpected exception", uoe); - return; - } - - sliceHandle.invokeExact(segment, 0); // should throw - } - } } diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java index e09eaab508a..0e75baeaa45 100644 --- a/test/jdk/java/foreign/TestLayouts.java +++ b/test/jdk/java/foreign/TestLayouts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -31,15 +31,13 @@ import java.lang.foreign.*; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; +import java.util.List; import java.util.function.LongFunction; import java.util.stream.Stream; import org.testng.annotations.*; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_INT; -import static java.lang.foreign.ValueLayout.JAVA_LONG; -import static java.lang.foreign.ValueLayout.JAVA_SHORT; +import static java.lang.foreign.ValueLayout.*; import static org.testng.Assert.*; public class TestLayouts { @@ -49,11 +47,53 @@ public class TestLayouts { layout.withBitAlignment(alignment); } + @Test(dataProvider = "basicLayoutsAndAddressAndGroups") + public void testEqualities(MemoryLayout layout) { + + // Use another Type + MemoryLayout differentType = MemoryLayout.paddingLayout(8); + assertFalse(layout.equals(differentType)); + + // Use another name + MemoryLayout differentName = layout.withName("CustomName"); + assertFalse(layout.equals(differentName)); + + // Use another alignment + MemoryLayout differentAlignment = layout.withBitAlignment(layout.bitAlignment() * 2); + assertFalse(layout.equals(differentAlignment)); + + // Swap endian + MemoryLayout differentOrder = JAVA_INT.withOrder(JAVA_INT.order() == ByteOrder.BIG_ENDIAN ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); + assertFalse(layout.equals(differentOrder)); + + // Something totally different + assertFalse(layout.equals("A")); + + // Null + assertFalse(layout.equals(null)); + + // Identity + assertTrue(layout.equals(layout)); + + assertFalse(layout.equals(MemoryLayout.sequenceLayout(13, JAVA_LONG))); + + MemoryLayout other = layout.withBitAlignment(128).withBitAlignment(layout.bitAlignment()); + assertTrue(layout.equals(other)); + + } + + public void testTargetLayoutEquals() { + MemoryLayout differentTargetLayout = ADDRESS.withTargetLayout(JAVA_CHAR); + assertFalse(ADDRESS.equals(differentTargetLayout)); + var equalButNotSame = ADDRESS.withTargetLayout(JAVA_INT).withTargetLayout(JAVA_CHAR); + assertTrue(differentTargetLayout.equals(equalButNotSame)); + } + @Test public void testIndexedSequencePath() { MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(seq);; VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement()); // init segment for (int i = 0 ; i < 10 ; i++) { @@ -75,15 +115,39 @@ public class TestLayouts { seq.withElementCount(-1); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testReshape() { + SequenceLayout layout = MemoryLayout.sequenceLayout(10, JAVA_INT); + layout.reshape(); + } + + @Test(dataProvider = "basicLayoutsAndAddressAndGroups", expectedExceptions = IllegalArgumentException.class) + public void testGroupIllegalAlignmentNotPowerOfTwo(MemoryLayout layout) { + layout.withBitAlignment(3); + } + + @Test(dataProvider = "basicLayoutsAndAddressAndGroups", expectedExceptions = IllegalArgumentException.class) + public void testGroupIllegalAlignmentNotGreaterOrEqualTo8(MemoryLayout layout) { + layout.withBitAlignment(4); + } + + @Test + public void testEqualsPadding() { + PaddingLayout paddingLayout = MemoryLayout.paddingLayout(16); + testEqualities(paddingLayout); + PaddingLayout paddingLayout2 = MemoryLayout.paddingLayout(32); + assertNotEquals(paddingLayout, paddingLayout2); + } + @Test public void testEmptyGroup() { MemoryLayout struct = MemoryLayout.structLayout(); assertEquals(struct.bitSize(), 0); - assertEquals(struct.bitAlignment(), 1); + assertEquals(struct.bitAlignment(), 8); MemoryLayout union = MemoryLayout.unionLayout(); assertEquals(union.bitSize(), 0); - assertEquals(union.bitAlignment(), 1); + assertEquals(union.bitAlignment(), 8); } @Test @@ -101,7 +165,7 @@ public class TestLayouts { @Test(dataProvider="basicLayouts") public void testPaddingNoAlign(MemoryLayout layout) { - assertEquals(MemoryLayout.paddingLayout(layout.bitSize()).bitAlignment(), 1); + assertEquals(MemoryLayout.paddingLayout(layout.bitSize()).bitAlignment(), 8); } @Test(dataProvider="basicLayouts") @@ -166,6 +230,39 @@ public class TestLayouts { MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE))); } + @Test + public void testPadding() { + var padding = MemoryLayout.paddingLayout(8); + assertEquals(padding.byteAlignment(), 1); + } + + @Test + public void testPaddingInStruct() { + var padding = MemoryLayout.paddingLayout(8); + var struct = MemoryLayout.structLayout(padding); + assertEquals(struct.byteAlignment(), 1); + } + + @Test + public void testPaddingIllegalBitSize() { + for (long bitSize : List.of(-8L, -1L, 0L, 1L, 7L)) { + try { + MemoryLayout.paddingLayout(bitSize); + fail("bitSize cannot be " + bitSize); + } catch (IllegalArgumentException ignore) { + // Happy path + } + } + } + + @Test + public void testStructToString() { + StructLayout padding = MemoryLayout.structLayout(JAVA_INT).withName("struct"); + assertEquals(padding.toString(), "[i32](struct)"); + var toStringUnaligned = padding.withBitAlignment(64).toString(); + assertEquals(toStringUnaligned, "64%[i32](struct)"); + } + @Test(dataProvider = "layoutKinds") public void testPadding(LayoutKind kind) { assertEquals(kind == LayoutKind.PADDING, kind.layout instanceof PaddingLayout); @@ -175,13 +272,44 @@ public class TestLayouts { public void testAlignmentString(MemoryLayout layout, long bitAlign) { long[] alignments = { 8, 16, 32, 64, 128 }; for (long a : alignments) { - if (layout.bitAlignment() == layout.bitSize()) { + if (layout.bitAlignment() == bitAlign) { assertFalse(layout.toString().contains("%")); - assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign); + if (a >= layout.bitAlignment()) { + assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign); + } } } } + @Test(dataProvider="layoutsAndAlignments") + public void testBadBitAlignment(MemoryLayout layout, long bitAlign) { + long[] alignments = { 8, 16, 32, 64, 128 }; + for (long a : alignments) { + if (a < bitAlign && !(layout instanceof ValueLayout)) { + assertThrows(IllegalArgumentException.class, () -> layout.withBitAlignment(a)); + } + } + } + + @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class) + public void testBadSequence(MemoryLayout layout, long bitAlign) { + layout = layout.withBitAlignment(layout.bitSize() * 2); // hyper-align + MemoryLayout.sequenceLayout(layout); + } + + @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class) + public void testBadStruct(MemoryLayout layout, long bitAlign) { + layout = layout.withBitAlignment(layout.bitSize() * 2); // hyper-align + MemoryLayout.structLayout(layout, layout); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testSequenceElement() { + SequenceLayout layout = MemoryLayout.sequenceLayout(10, JAVA_INT); + // Step must be != 0 + PathElement.sequenceElement(3, 0); + } + @DataProvider(name = "badAlignments") public Object[][] layoutsAndBadAlignments() { LayoutKind[] layoutKinds = LayoutKind.values(); @@ -248,6 +376,20 @@ public class TestLayouts { .toArray(Object[][]::new); } + @DataProvider(name = "basicLayoutsAndAddress") + public Object[][] basicLayoutsAndAddress() { + return Stream.concat(Stream.of(basicLayouts), Stream.of(ADDRESS)) + .map(l -> new Object[] { l }) + .toArray(Object[][]::new); + } + + @DataProvider(name = "basicLayoutsAndAddressAndGroups") + public Object[][] basicLayoutsAndAddressAndGroups() { + return Stream.concat(Stream.concat(Stream.of(basicLayouts), Stream.of(ADDRESS)), groupLayoutStream()) + .map(l -> new Object[] { l }) + .toArray(Object[][]::new); + } + @DataProvider(name = "layoutsAndAlignments") public Object[][] layoutsAndAlignments() { Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 4][]; @@ -271,6 +413,39 @@ public class TestLayouts { return layoutsAndAlignments; } + @DataProvider(name = "groupLayouts") + public Object[][] groupLayouts() { + return groupLayoutStream() + .map(l -> new Object[] { l }) + .toArray(Object[][]::new); + } + + @DataProvider(name = "validCarriers") + public Object[][] validCarriers() { + return Stream.of( + boolean.class, + byte.class, + char.class, + short.class, + int.class, + long.class, + float.class, + double.class, + MemorySegment.class + ) + .map(l -> new Object[]{l}) + .toArray(Object[][]::new); + } + + static Stream groupLayoutStream() { + return Stream.of( + MemoryLayout.sequenceLayout(10, JAVA_INT), + MemoryLayout.sequenceLayout(JAVA_INT), + MemoryLayout.structLayout(JAVA_INT, MemoryLayout.paddingLayout(32), JAVA_LONG), + MemoryLayout.unionLayout(JAVA_LONG, JAVA_DOUBLE) + ); + } + static MemoryLayout[] basicLayouts = { ValueLayout.JAVA_BYTE, ValueLayout.JAVA_CHAR, diff --git a/test/jdk/java/foreign/TestMemoryAccess.java b/test/jdk/java/foreign/TestMemoryAccess.java index 7028635b682..f4e0a5be5ff 100644 --- a/test/jdk/java/foreign/TestMemoryAccess.java +++ b/test/jdk/java/foreign/TestMemoryAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -30,13 +30,8 @@ * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestMemoryAccess */ -import java.lang.foreign.Arena; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; +import java.lang.foreign.*; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SequenceLayout; -import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; @@ -91,8 +86,8 @@ public class TestMemoryAccess { private void testAccessInternal(Function viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) { MemorySegment outer_segment; - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout, arena.scope())); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = viewFactory.apply(arena.allocate(layout)); boolean isRO = segment.isReadOnly(); try { checker.check(handle, segment); @@ -123,8 +118,8 @@ public class TestMemoryAccess { private void testArrayAccessInternal(Function viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) { MemorySegment outer_segment; - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, arena.scope())); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = viewFactory.apply(arena.allocate(seq)); boolean isRO = segment.isReadOnly(); try { for (int i = 0; i < seq.elementCount(); i++) { @@ -192,8 +187,8 @@ public class TestMemoryAccess { private void testMatrixAccessInternal(Function viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) { MemorySegment outer_segment; - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, arena.scope())); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = viewFactory.apply(arena.allocate(seq)); boolean isRO = segment.isReadOnly(); try { for (int i = 0; i < seq.elementCount(); i++) { diff --git a/test/jdk/java/foreign/TestMemoryAccessInstance.java b/test/jdk/java/foreign/TestMemoryAccessInstance.java index e80c170ca65..d644a5ad904 100644 --- a/test/jdk/java/foreign/TestMemoryAccessInstance.java +++ b/test/jdk/java/foreign/TestMemoryAccessInstance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -27,9 +27,8 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance */ -import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -80,8 +79,8 @@ public class TestMemoryAccessInstance { } void test() { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(128, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(128, 1); ByteBuffer buffer = segment.asByteBuffer(); T t = transform.apply(segment); segmentSetter.set(t, layout, 8, value); @@ -93,8 +92,8 @@ public class TestMemoryAccessInstance { @SuppressWarnings("unchecked") void testHyperAligned() { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(64, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(64, 1); T t = transform.apply(segment); L alignedLayout = (L)layout.withBitAlignment(layout.byteSize() * 8 * 2); try { @@ -136,7 +135,9 @@ public class TestMemoryAccessInstance { @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") public void badHeapSegmentSet() { - MemorySegment targetSegment = MemorySegment.allocateNative(ValueLayout.ADDRESS.byteSize(), SegmentScope.auto()); + long byteSize = ValueLayout.ADDRESS.byteSize(); + Arena scope = Arena.ofAuto(); + MemorySegment targetSegment = scope.allocate(byteSize, 1); MemorySegment segment = MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); targetSegment.set(ValueLayout.ADDRESS, 0, segment); // should throw } @@ -144,7 +145,9 @@ public class TestMemoryAccessInstance { @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") public void badHeapSegmentSetAtIndex() { - MemorySegment targetSegment = MemorySegment.allocateNative(ValueLayout.ADDRESS.byteSize(), SegmentScope.auto()); + long byteSize = ValueLayout.ADDRESS.byteSize(); + Arena scope = Arena.ofAuto(); + MemorySegment targetSegment = scope.allocate(byteSize, 1); MemorySegment segment = MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); targetSegment.setAtIndex(ValueLayout.ADDRESS, 0, segment); // should throw } @@ -159,7 +162,7 @@ public class TestMemoryAccessInstance { MemorySegment::get, MemorySegment::set, ByteBuffer::get, ByteBuffer::put) }, - {"bool", Accessor.ofSegment(ValueLayout.JAVA_BOOLEAN, false, + {"boolean", Accessor.ofSegment(ValueLayout.JAVA_BOOLEAN, false, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0)) }, @@ -167,6 +170,10 @@ public class TestMemoryAccessInstance { MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v)) }, + {"short", Accessor.ofSegment(ValueLayout.JAVA_SHORT, (short) 42, + MemorySegment::get, MemorySegment::set, + (bb, pos) -> bb.order(NE).getShort(pos), (bb, pos, v) -> bb.order(NE).putShort(pos, v)) + }, {"int", Accessor.ofSegment(ValueLayout.JAVA_INT, 42, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v)) @@ -201,10 +208,22 @@ public class TestMemoryAccessInstance { }) }, + {"byte/index", Accessor.ofSegment(ValueLayout.JAVA_BYTE, (byte) 42, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).get(pos), (bb, pos, v) -> bb.order(NE).put(pos, v)) + }, + {"boolean/index", Accessor.ofSegment(ValueLayout.JAVA_BOOLEAN, true, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).get(pos) != 0, (bb, pos, v) -> bb.order(NE).put(pos, (byte) (v ? 1 : 0))) + }, {"char/index", Accessor.ofSegment(ValueLayout.JAVA_CHAR, (char) 42, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v)) }, + {"short/index", Accessor.ofSegment(ValueLayout.JAVA_SHORT, (short) 42, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).getShort(pos * 2), (bb, pos, v) -> bb.order(NE).putShort(pos * 2, v)) + }, {"int/index", Accessor.ofSegment(ValueLayout.JAVA_INT, 42, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v)) diff --git a/test/jdk/java/foreign/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java index cc92085422b..db39b1d0432 100644 --- a/test/jdk/java/foreign/TestMemoryAlignment.java +++ b/test/jdk/java/foreign/TestMemoryAlignment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -27,13 +27,8 @@ * @run testng TestMemoryAlignment */ -import java.lang.foreign.Arena; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; +import java.lang.foreign.*; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SequenceLayout; -import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; import java.util.stream.LongStream; @@ -52,8 +47,8 @@ public class TestMemoryAlignment { ValueLayout aligned = layout.withBitAlignment(align); assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws VarHandle vh = aligned.varHandle(); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(aligned, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(aligned);; vh.set(segment, -42); int val = (int)vh.get(segment); assertEquals(val, -42); @@ -67,11 +62,11 @@ public class TestMemoryAlignment { .withOrder(ByteOrder.BIG_ENDIAN); assertEquals(layout.bitAlignment(), 32); ValueLayout aligned = layout.withBitAlignment(align); - MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned); - assertEquals(alignedGroup.bitAlignment(), align); - VarHandle vh = aligned.varHandle(); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(alignedGroup, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned); + assertEquals(alignedGroup.bitAlignment(), align); + VarHandle vh = aligned.varHandle(); + MemorySegment segment = arena.allocate(alignedGroup);; vh.set(segment.asSlice(1L), -42); assertEquals(align, 8); //this is the only case where access is aligned } catch (IllegalArgumentException ex) { @@ -83,27 +78,27 @@ public class TestMemoryAlignment { public void testUnalignedPath(long align) { MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN); MemoryLayout aligned = layout.withBitAlignment(align).withName("value"); - GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned); try { + GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned); alignedGroup.varHandle(PathElement.groupElement("value")); assertEquals(align, 8); //this is the only case where path is aligned - } catch (UnsupportedOperationException ex) { + } catch (IllegalArgumentException ex) { assertNotEquals(align, 8); //if align != 8, path is always unaligned } } @Test(dataProvider = "alignments") public void testUnalignedSequence(long align) { - SequenceLayout layout = MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withBitAlignment(align)); try { + SequenceLayout layout = MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withBitAlignment(align)); VarHandle vh = layout.varHandle(PathElement.sequenceElement()); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(layout);; for (long i = 0 ; i < 5 ; i++) { vh.set(segment, i, -42); } } - } catch (UnsupportedOperationException ex) { + } catch (IllegalArgumentException ex) { assertTrue(align > 32); //if align > 32, access is always unaligned (for some elements) } } @@ -121,8 +116,8 @@ public class TestMemoryAlignment { VarHandle vh_c = g.varHandle(PathElement.groupElement("a")); VarHandle vh_s = g.varHandle(PathElement.groupElement("b")); VarHandle vh_i = g.varHandle(PathElement.groupElement("c")); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(g, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(g);; vh_c.set(segment, Byte.MIN_VALUE); assertEquals(vh_c.get(segment), Byte.MIN_VALUE); vh_s.set(segment, Short.MIN_VALUE); diff --git a/test/jdk/java/foreign/TestMemorySession.java b/test/jdk/java/foreign/TestMemorySession.java index 6501978f725..e55ad572b46 100644 --- a/test/jdk/java/foreign/TestMemorySession.java +++ b/test/jdk/java/foreign/TestMemorySession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -24,19 +24,18 @@ /* * @test * @enablePreview - * @modules java.base/jdk.internal.ref java.base/jdk.internal.foreign + * @modules java.base/jdk.internal.foreign * @run testng/othervm TestMemorySession */ import java.lang.foreign.Arena; -import java.lang.foreign.SegmentScope; - import jdk.internal.foreign.MemorySessionImpl; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.*; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -51,10 +50,10 @@ public class TestMemorySession { @Test public void testConfined() { AtomicInteger acc = new AtomicInteger(); - Arena arena = Arena.openConfined(); + Arena arena = Arena.ofConfined(); for (int i = 0 ; i < N_THREADS ; i++) { int delta = i; - addCloseAction(arena.scope(), () -> acc.addAndGet(delta)); + addCloseAction(arena, () -> acc.addAndGet(delta)); } assertEquals(acc.get(), 0); @@ -63,17 +62,17 @@ public class TestMemorySession { } @Test(dataProvider = "sharedSessions") - public void testSharedSingleThread(SessionSupplier sessionSupplier) { + public void testSharedSingleThread(ArenaSupplier arenaSupplier) { AtomicInteger acc = new AtomicInteger(); - SegmentScope session = sessionSupplier.get(); + Arena session = arenaSupplier.get(); for (int i = 0 ; i < N_THREADS ; i++) { int delta = i; addCloseAction(session, () -> acc.addAndGet(delta)); } assertEquals(acc.get(), 0); - if (!SessionSupplier.isImplicit(session)) { - SessionSupplier.close(session); + if (!TestMemorySession.ArenaSupplier.isImplicit(session)) { + TestMemorySession.ArenaSupplier.close(session); assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum()); } else { session = null; @@ -85,11 +84,11 @@ public class TestMemorySession { } @Test(dataProvider = "sharedSessions") - public void testSharedMultiThread(SessionSupplier sessionSupplier) { + public void testSharedMultiThread(ArenaSupplier arenaSupplier) { AtomicInteger acc = new AtomicInteger(); List threads = new ArrayList<>(); - SegmentScope session = sessionSupplier.get(); - AtomicReference sessionRef = new AtomicReference<>(session); + Arena session = arenaSupplier.get(); + AtomicReference sessionRef = new AtomicReference<>(session); for (int i = 0 ; i < N_THREADS ; i++) { int delta = i; Thread thread = new Thread(() -> { @@ -109,10 +108,10 @@ public class TestMemorySession { // if no cleaner, close - not all segments might have been added to the session! // if cleaner, don't unset the session - after all, the session is kept alive by threads - if (!SessionSupplier.isImplicit(session)) { + if (!TestMemorySession.ArenaSupplier.isImplicit(session)) { while (true) { try { - SessionSupplier.close(session); + TestMemorySession.ArenaSupplier.close(session); break; } catch (IllegalStateException ise) { // session is acquired (by add) - wait some more @@ -128,7 +127,7 @@ public class TestMemorySession { } }); - if (!SessionSupplier.isImplicit(session)) { + if (!TestMemorySession.ArenaSupplier.isImplicit(session)) { assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum()); } else { session = null; @@ -142,11 +141,11 @@ public class TestMemorySession { @Test public void testLockSingleThread() { - Arena arena = Arena.openConfined(); + Arena arena = Arena.ofConfined(); List handles = new ArrayList<>(); for (int i = 0 ; i < N_THREADS ; i++) { - Arena handle = Arena.openConfined(); - keepAlive(handle.scope(), arena.scope()); + Arena handle = Arena.ofConfined(); + keepAlive(handle, arena); handles.add(handle); } @@ -165,12 +164,12 @@ public class TestMemorySession { @Test public void testLockSharedMultiThread() { - Arena arena = Arena.openShared(); + Arena arena = Arena.ofShared(); AtomicInteger lockCount = new AtomicInteger(); for (int i = 0 ; i < N_THREADS ; i++) { new Thread(() -> { - try (Arena handle = Arena.openConfined()) { - keepAlive(handle.scope(), arena.scope()); + try (Arena handle = Arena.ofConfined()) { + keepAlive(handle, arena); lockCount.incrementAndGet(); waitSomeTime(); lockCount.decrementAndGet(); @@ -193,19 +192,19 @@ public class TestMemorySession { @Test public void testCloseEmptyConfinedSession() { - Arena.openConfined().close(); + Arena.ofConfined().close(); } @Test public void testCloseEmptySharedSession() { - Arena.openShared().close(); + Arena.ofShared().close(); } @Test public void testCloseConfinedLock() { - Arena arena = Arena.openConfined(); - Arena handle = Arena.openConfined(); - keepAlive(handle.scope(), arena.scope()); + Arena arena = Arena.ofConfined(); + Arena handle = Arena.ofConfined(); + keepAlive(handle, arena); AtomicReference failure = new AtomicReference<>(); Thread t = new Thread(() -> { try { @@ -225,32 +224,32 @@ public class TestMemorySession { } @Test(dataProvider = "allSessions") - public void testSessionAcquires(SessionSupplier sessionSupplier) { - SegmentScope session = sessionSupplier.get(); + public void testSessionAcquires(ArenaSupplier ArenaSupplier) { + Arena session = ArenaSupplier.get(); acquireRecursive(session, 5); - if (!SessionSupplier.isImplicit(session)) - SessionSupplier.close(session); + if (!TestMemorySession.ArenaSupplier.isImplicit(session)) + TestMemorySession.ArenaSupplier.close(session); } - private void acquireRecursive(SegmentScope session, int acquireCount) { - try (Arena arena = Arena.openConfined()) { - keepAlive(arena.scope(), session); + private void acquireRecursive(Arena session, int acquireCount) { + try (Arena arena = Arena.ofConfined()) { + keepAlive(arena, session); if (acquireCount > 0) { // recursive acquire acquireRecursive(session, acquireCount - 1); } - if (!SessionSupplier.isImplicit(session)) { - assertThrows(IllegalStateException.class, () -> SessionSupplier.close(session)); + if (!ArenaSupplier.isImplicit(session)) { + assertThrows(IllegalStateException.class, () -> ArenaSupplier.close(session)); } } } @Test public void testConfinedSessionWithImplicitDependency() { - Arena root = Arena.openConfined(); + Arena root = Arena.ofConfined(); // Create many implicit sessions which depend on 'root', and let them become unreachable. for (int i = 0; i < N_THREADS; i++) { - keepAlive(SegmentScope.auto(), root.scope()); + keepAlive(Arena.ofAuto(), root); } // Now let's keep trying to close 'root' until we succeed. This is trickier than it seems: cleanup action // might be called from another thread (the Cleaner thread), so that the confined session lock count is updated racily. @@ -262,8 +261,8 @@ public class TestMemorySession { } catch (IllegalStateException ex) { kickGC(); for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread - try (Arena arena = Arena.openConfined()) { - keepAlive(arena.scope(), root.scope()); + try (Arena arena = Arena.ofConfined()) { + keepAlive(arena, root); // dummy } } @@ -274,19 +273,19 @@ public class TestMemorySession { @Test public void testConfinedSessionWithSharedDependency() { - Arena root = Arena.openConfined(); + Arena root = Arena.ofConfined(); List threads = new ArrayList<>(); // Create many implicit sessions which depend on 'root', and let them become unreachable. for (int i = 0; i < N_THREADS; i++) { - Arena arena = Arena.openShared(); // create session inside same thread! - keepAlive(arena.scope(), root.scope()); + Arena arena = Arena.ofShared(); // create session inside same thread! + keepAlive(arena, root); Thread t = new Thread(arena::close); // close from another thread! threads.add(t); t.start(); } for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread - try (Arena arena = Arena.openConfined()) { - keepAlive(arena.scope(), root.scope()); + try (Arena arena = Arena.ofConfined()) { + keepAlive(arena, root); // dummy } } @@ -302,6 +301,25 @@ public class TestMemorySession { root.close(); } + @Test(dataProvider = "nonCloseableSessions") + public void testNonCloseableSessions(ArenaSupplier arenaSupplier) { + var arena = arenaSupplier.get(); + var sessionImpl = ((MemorySessionImpl) arena.scope()); + assertFalse(sessionImpl.isCloseable()); + assertThrows(UnsupportedOperationException.class, () -> + sessionImpl.close()); + } + + @Test(dataProvider = "allSessionsAndGlobal") + public void testIsCloseableBy(ArenaSupplier arenaSupplier) { + var arena = arenaSupplier.get(); + var sessionImpl = ((MemorySessionImpl) arena.scope()); + assertEquals(sessionImpl.isCloseableBy(Thread.currentThread()), sessionImpl.isCloseable()); + Thread otherThread = new Thread(); + boolean isCloseableByOther = sessionImpl.isCloseable() && !"ConfinedSession".equals(sessionImpl.getClass().getSimpleName()); + assertEquals(sessionImpl.isCloseableBy(otherThread), isCloseableByOther); + } + private void waitSomeTime() { try { Thread.sleep(10); @@ -321,55 +339,78 @@ public class TestMemorySession { @DataProvider static Object[][] drops() { return new Object[][] { - { (Supplier) Arena::openConfined}, - { (Supplier) Arena::openShared}, + { (Supplier) Arena::ofConfined}, + { (Supplier) Arena::ofShared}, }; } - private void keepAlive(SegmentScope child, SegmentScope parent) { - MemorySessionImpl parentImpl = (MemorySessionImpl) parent; + private void keepAlive(Arena child, Arena parent) { + MemorySessionImpl parentImpl = MemorySessionImpl.toMemorySession(parent); parentImpl.acquire0(); addCloseAction(child, parentImpl::release0); } - private void addCloseAction(SegmentScope session, Runnable action) { - MemorySessionImpl sessionImpl = (MemorySessionImpl) session; + private void addCloseAction(Arena session, Runnable action) { + MemorySessionImpl sessionImpl = MemorySessionImpl.toMemorySession(session); sessionImpl.addCloseAction(action); } - interface SessionSupplier extends Supplier { + interface ArenaSupplier extends Supplier { - static void close(SegmentScope session) { - ((MemorySessionImpl)session).close(); + static void close(Arena arena) { + MemorySessionImpl.toMemorySession(arena).close(); } - static boolean isImplicit(SegmentScope session) { - return !((MemorySessionImpl)session).isCloseable(); + static boolean isImplicit(Arena arena) { + return !MemorySessionImpl.toMemorySession(arena).isCloseable(); } - static SessionSupplier ofImplicit() { - return SegmentScope::auto; + static ArenaSupplier ofAuto() { + return Arena::ofAuto; } - static SessionSupplier ofArena(Supplier arenaSupplier) { - return () -> arenaSupplier.get().scope(); + static ArenaSupplier ofGlobal() { + return Arena::global; + } + + static ArenaSupplier ofArena(Supplier arenaSupplier) { + return arenaSupplier::get; } } @DataProvider(name = "sharedSessions") static Object[][] sharedSessions() { return new Object[][] { - { SessionSupplier.ofArena(Arena::openShared) }, - { SessionSupplier.ofImplicit() }, + { ArenaSupplier.ofArena(Arena::ofShared) }, + { ArenaSupplier.ofAuto() }, }; } @DataProvider(name = "allSessions") static Object[][] allSessions() { return new Object[][] { - { SessionSupplier.ofArena(Arena::openConfined) }, - { SessionSupplier.ofArena(Arena::openShared) }, - { SessionSupplier.ofImplicit() }, + { ArenaSupplier.ofArena(Arena::ofConfined) }, + { ArenaSupplier.ofArena(Arena::ofShared) }, + { ArenaSupplier.ofAuto() }, }; } + + @DataProvider(name = "nonCloseableSessions") + static Object[][] nonCloseableSessions() { + return new Object[][] { + { ArenaSupplier.ofGlobal() }, + { ArenaSupplier.ofAuto() } + }; + } + + @DataProvider(name = "allSessionsAndGlobal") + static Object[][] allSessionsAndGlobal() { + return new Object[][] { + { ArenaSupplier.ofArena(Arena::ofConfined) }, + { ArenaSupplier.ofArena(Arena::ofShared) }, + { ArenaSupplier.ofAuto() }, + { ArenaSupplier.ofGlobal() }, + }; + } + } diff --git a/test/jdk/java/foreign/TestMismatch.java b/test/jdk/java/foreign/TestMismatch.java index e7a381b9ce5..ed0faeef0e7 100644 --- a/test/jdk/java/foreign/TestMismatch.java +++ b/test/jdk/java/foreign/TestMismatch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -28,7 +28,6 @@ */ import java.lang.foreign.Arena; -import java.lang.foreign.SegmentScope; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -172,8 +171,8 @@ public class TestMismatch { public void testEmpty() { var s1 = MemorySegment.ofArray(new byte[0]); assertEquals(s1.mismatch(s1), -1); - try (Arena arena = Arena.openConfined()) { - var nativeSegment = MemorySegment.allocateNative(4, 4, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + var nativeSegment = arena.allocate(4, 4);; var s2 = nativeSegment.asSlice(0, 0); assertEquals(s1.mismatch(s2), -1); assertEquals(s2.mismatch(s1), -1); @@ -184,9 +183,9 @@ public class TestMismatch { public void testLarge() { // skip if not on 64 bits if (ValueLayout.ADDRESS.byteSize() > 32) { - try (Arena arena = Arena.openConfined()) { - var s1 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, arena.scope());; - var s2 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + var s1 = arena.allocate((long) Integer.MAX_VALUE + 10L, 8);; + var s2 = arena.allocate((long) Integer.MAX_VALUE + 10L, 8);; assertEquals(s1.mismatch(s1), -1); assertEquals(s1.mismatch(s2), -1); assertEquals(s2.mismatch(s1), -1); @@ -228,9 +227,9 @@ public class TestMismatch { @Test public void testClosed() { MemorySegment s1, s2; - try (Arena arena = Arena.openConfined()) { - s1 = MemorySegment.allocateNative(4, 1, arena.scope());; - s2 = MemorySegment.allocateNative(4, 1, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + s1 = arena.allocate(4, 1); + s2 = arena.allocate(4, 1);; } assertThrows(ISE, () -> s1.mismatch(s1)); assertThrows(ISE, () -> s1.mismatch(s2)); @@ -239,8 +238,8 @@ public class TestMismatch { @Test public void testThreadAccess() throws Exception { - try (Arena arena = Arena.openConfined()) { - var segment = MemorySegment.allocateNative(4, 1, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + var segment = arena.allocate(4, 1);; { AtomicReference exception = new AtomicReference<>(); Runnable action = () -> { @@ -281,7 +280,7 @@ public class TestMismatch { } enum SegmentKind { - NATIVE(i -> MemorySegment.allocateNative(i, SegmentScope.auto())), + NATIVE(i -> Arena.ofAuto().allocate(i, 1)), ARRAY(i -> MemorySegment.ofArray(new byte[i])); final IntFunction segmentFactory; diff --git a/test/jdk/java/foreign/TestNULLAddress.java b/test/jdk/java/foreign/TestNULLAddress.java index 00679decdec..1c2756e8ac6 100644 --- a/test/jdk/java/foreign/TestNULLAddress.java +++ b/test/jdk/java/foreign/TestNULLAddress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -64,9 +64,9 @@ public class TestNULLAddress { } @Test - public void testNULLReturn_unbounded() throws Throwable { + public void testNULLReturn_target() throws Throwable { MethodHandle mh = LINKER.downcallHandle(SymbolLookup.loaderLookup().find("get_null").get(), - FunctionDescriptor.of(ValueLayout.ADDRESS.asUnbounded())); + FunctionDescriptor.of(ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT))); MemorySegment ret = (MemorySegment)mh.invokeExact(); assertTrue(ret.equals(MemorySegment.NULL)); } diff --git a/test/jdk/java/foreign/TestNative.java b/test/jdk/java/foreign/TestNative.java index ee2fd7f6352..ca535f7e3f6 100644 --- a/test/jdk/java/foreign/TestNative.java +++ b/test/jdk/java/foreign/TestNative.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -29,13 +29,9 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestNative */ -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemoryLayout; +import java.lang.foreign.*; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SequenceLayout; -import java.lang.foreign.ValueLayout; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -145,8 +141,8 @@ public class TestNative extends NativeTestHelper { @Test(dataProvider="nativeAccessOps") public void testNativeAccess(Consumer checker, Consumer initializer, SequenceLayout seq) { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(seq);; initializer.accept(segment); checker.accept(segment); } @@ -155,8 +151,8 @@ public class TestNative extends NativeTestHelper { @Test(dataProvider="buffers") public void testNativeCapacity(Function bufferFunction, int elemSize) { int capacity = (int)doubles.byteSize(); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(doubles, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(doubles);; ByteBuffer bb = segment.asByteBuffer(); Buffer buf = bufferFunction.apply(bb); int expected = capacity / elemSize; @@ -168,9 +164,9 @@ public class TestNative extends NativeTestHelper { @Test public void testDefaultAccessModes() { MemorySegment addr = allocateMemory(12); - try (Arena arena = Arena.openConfined()) { - MemorySegment mallocSegment = MemorySegment.ofAddress(addr.address(), 12, - arena.scope(), () -> freeMemory(addr)); + try (Arena arena = Arena.ofConfined()) { + MemorySegment mallocSegment = addr.asSlice(0, 12) + .reinterpret(arena, TestNative::freeMemory); assertFalse(mallocSegment.isReadOnly()); } } @@ -179,9 +175,9 @@ public class TestNative extends NativeTestHelper { public void testMallocSegment() { MemorySegment addr = allocateMemory(12); MemorySegment mallocSegment = null; - try (Arena arena = Arena.openConfined()) { - mallocSegment = MemorySegment.ofAddress(addr.address(), 12, - arena.scope(), () -> freeMemory(addr)); + try (Arena arena = Arena.ofConfined()) { + mallocSegment = addr.asSlice(0, 12) + .reinterpret(arena, TestNative::freeMemory); assertEquals(mallocSegment.byteSize(), 12); //free here } @@ -196,11 +192,12 @@ public class TestNative extends NativeTestHelper { freeMemory(addr); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testBadResize() { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(4, 1, arena.scope());; - MemorySegment.ofAddress(segment.address(), -1, SegmentScope.global()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(4, 1); + assertThrows(IllegalArgumentException.class, () -> segment.reinterpret(-1)); + assertThrows(IllegalArgumentException.class, () -> segment.reinterpret(-1, Arena.ofAuto(), null)); } } diff --git a/test/jdk/java/foreign/TestNulls.java b/test/jdk/java/foreign/TestNulls.java index 21796b38540..31c9031d4b3 100644 --- a/test/jdk/java/foreign/TestNulls.java +++ b/test/jdk/java/foreign/TestNulls.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -39,7 +39,7 @@ import org.testng.annotations.NoInjection; import org.testng.annotations.Test; import java.lang.constant.Constable; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -56,7 +56,6 @@ import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.function.UnaryOperator; @@ -95,35 +94,22 @@ public class TestNulls { ValueLayout.OfFloat.class, ValueLayout.OfLong.class, ValueLayout.OfDouble.class, - ValueLayout.OfAddress.class, + AddressLayout.class, + PaddingLayout.class, GroupLayout.class, + StructLayout.class, + UnionLayout.class, Linker.class, - VaList.class, - VaList.Builder.class, + Linker.Option.class, FunctionDescriptor.class, SegmentAllocator.class, - SegmentScope.class, + MemorySegment.Scope.class, SymbolLookup.class }; static final Set EXCLUDE_LIST = Set.of( - "java.lang.foreign.MemorySegment/ofAddress(long,long,java.lang.foreign.SegmentScope,java.lang.Runnable)/3/0", - "java.lang.foreign.MemorySegment.MemorySession/openConfined(java.lang.ref.Cleaner)/0/0", - "java.lang.foreign.MemorySegment.MemorySession/openShared(java.lang.ref.Cleaner)/0/0", - "java.lang.foreign.MemoryLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.SequenceLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfAddress/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfBoolean/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfByte/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfChar/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfShort/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfInt/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfFloat/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfLong/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.ValueLayout$OfDouble/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.GroupLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "java.lang.foreign.FunctionDescriptor/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0" + "java.lang.foreign.MemorySegment/reinterpret(java.lang.foreign.Arena,java.util.function.Consumer)/1/0", + "java.lang.foreign.MemorySegment/reinterpret(long,java.lang.foreign.Arena,java.util.function.Consumer)/2/0" ); static final Set OBJECT_METHODS = Stream.of(Object.class.getMethods()) @@ -166,7 +152,7 @@ public class TestNulls { addDefaultMapping(MethodType.class, MethodType.methodType(void.class)); addDefaultMapping(MemoryLayout.class, ValueLayout.JAVA_INT); addDefaultMapping(ValueLayout.class, ValueLayout.JAVA_INT); - addDefaultMapping(ValueLayout.OfAddress.class, ValueLayout.ADDRESS); + addDefaultMapping(AddressLayout.class, ValueLayout.ADDRESS); addDefaultMapping(ValueLayout.OfByte.class, ValueLayout.JAVA_BYTE); addDefaultMapping(ValueLayout.OfBoolean.class, ValueLayout.JAVA_BOOLEAN); addDefaultMapping(ValueLayout.OfChar.class, ValueLayout.JAVA_CHAR); @@ -175,33 +161,21 @@ public class TestNulls { addDefaultMapping(ValueLayout.OfFloat.class, ValueLayout.JAVA_FLOAT); addDefaultMapping(ValueLayout.OfLong.class, JAVA_LONG); addDefaultMapping(ValueLayout.OfDouble.class, ValueLayout.JAVA_DOUBLE); + addDefaultMapping(PaddingLayout.class, MemoryLayout.paddingLayout(32)); addDefaultMapping(GroupLayout.class, MemoryLayout.structLayout(ValueLayout.JAVA_INT)); + addDefaultMapping(StructLayout.class, MemoryLayout.structLayout(ValueLayout.JAVA_INT)); + addDefaultMapping(UnionLayout.class, MemoryLayout.unionLayout(ValueLayout.JAVA_INT)); addDefaultMapping(SequenceLayout.class, MemoryLayout.sequenceLayout(1, ValueLayout.JAVA_INT)); addDefaultMapping(SymbolLookup.class, SymbolLookup.loaderLookup()); addDefaultMapping(MemorySegment.class, MemorySegment.ofArray(new byte[10])); addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid()); addDefaultMapping(Linker.class, Linker.nativeLinker()); - addDefaultMapping(VaList.class, VaListHelper.vaList); - addDefaultMapping(VaList.Builder.class, VaListHelper.vaListBuilder); - addDefaultMapping(Arena.class, Arena.openConfined()); - addDefaultMapping(SegmentScope.class, SegmentScope.auto()); + addDefaultMapping(Arena.class, Arena.ofConfined()); + addDefaultMapping(MemorySegment.Scope.class, Arena.ofAuto().scope()); addDefaultMapping(SegmentAllocator.class, SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[10]))); addDefaultMapping(Supplier.class, () -> null); addDefaultMapping(ClassLoader.class, TestNulls.class.getClassLoader()); - } - - static class VaListHelper { - static final VaList vaList; - static final VaList.Builder vaListBuilder; - - static { - AtomicReference builderRef = new AtomicReference<>(); - vaList = VaList.make(b -> { - builderRef.set(b); - b.addVarg(JAVA_LONG, 42L); - }, SegmentScope.auto()); - vaListBuilder = builderRef.get(); - } + addDefaultMapping(Thread.UncaughtExceptionHandler.class, (thread, ex) -> {}); } static final Map, Object[]> REPLACEMENT_VALUES = new HashMap<>(); diff --git a/test/jdk/java/foreign/TestScopedOperations.java b/test/jdk/java/foreign/TestScopedOperations.java index 35b550ed385..c4673a73946 100644 --- a/test/jdk/java/foreign/TestScopedOperations.java +++ b/test/jdk/java/foreign/TestScopedOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -29,11 +29,7 @@ */ import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.VaList; import java.lang.foreign.ValueLayout; import org.testng.annotations.DataProvider; @@ -51,8 +47,6 @@ import java.util.function.Consumer; import java.util.function.Function; import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_INT; -import static java.lang.foreign.ValueLayout.JAVA_LONG; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; @@ -74,8 +68,8 @@ public class TestScopedOperations { @Test(dataProvider = "scopedOperations") public void testOpAfterClose(String name, ScopedOperation scopedOperation) { - Arena arena = Arena.openConfined(); - Z obj = scopedOperation.apply(arena.scope()); + Arena arena = Arena.ofConfined(); + Z obj = scopedOperation.apply(arena); arena.close(); try { scopedOperation.accept(obj); @@ -87,8 +81,8 @@ public class TestScopedOperations { @Test(dataProvider = "scopedOperations") public void testOpOutsideConfinement(String name, ScopedOperation scopedOperation) { - try (Arena arena = Arena.openConfined()) { - Z obj = scopedOperation.apply(arena.scope()); + try (Arena arena = Arena.ofConfined()) { + Z obj = scopedOperation.apply(arena); AtomicReference failed = new AtomicReference<>(); Thread t = new Thread(() -> { try { @@ -111,7 +105,7 @@ public class TestScopedOperations { static { // session operations - ScopedOperation.ofScope(session -> MemorySegment.allocateNative(100, session), "MemorySession::allocate");; + ScopedOperation.ofScope(session -> session.allocate(100, 1), "MemorySession::allocate"); ScopedOperation.ofScope(session -> { try (FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) { fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 10L, session); @@ -119,41 +113,30 @@ public class TestScopedOperations { fail(); } }, "FileChannel::map"); - ScopedOperation.ofScope(session -> VaList.make(b -> b.addVarg(JAVA_INT, 42), session), "VaList::make"); - ScopedOperation.ofScope(session -> VaList.ofAddress(42, session), "VaList::make"); // segment operations ScopedOperation.ofSegment(s -> s.toArray(JAVA_BYTE), "MemorySegment::toArray(BYTE)"); ScopedOperation.ofSegment(s -> s.copyFrom(s), "MemorySegment::copyFrom"); ScopedOperation.ofSegment(s -> s.mismatch(s), "MemorySegment::mismatch"); ScopedOperation.ofSegment(s -> s.fill((byte) 0), "MemorySegment::fill"); - // valist operations - ScopedOperation.ofVaList(VaList::copy, "VaList::copy"); - ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.ADDRESS), "VaList::nextVarg/address"); - ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.JAVA_INT), "VaList::nextVarg/int"); - ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.JAVA_LONG), "VaList::nextVarg/long"); - ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.JAVA_DOUBLE), "VaList::nextVarg/double"); - ScopedOperation.ofVaList(VaList::skip, "VaList::skip"); - ScopedOperation.ofVaList(list -> list.nextVarg(MemoryLayout.structLayout(ValueLayout.JAVA_INT), - SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[4]))), "VaList::nextVargs/segment"); // allocator operations - ScopedOperation.ofAllocator(a -> a.allocate(1), "NativeAllocator::allocate/size"); - ScopedOperation.ofAllocator(a -> a.allocate(1, 1), "NativeAllocator::allocate/size/align"); - ScopedOperation.ofAllocator(a -> a.allocate(JAVA_BYTE), "NativeAllocator::allocate/layout"); - ScopedOperation.ofAllocator(a -> a.allocate(JAVA_BYTE, (byte) 0), "NativeAllocator::allocate/byte"); - ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_CHAR, (char) 0), "NativeAllocator::allocate/char"); - ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_SHORT, (short) 0), "NativeAllocator::allocate/short"); - ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_INT, 0), "NativeAllocator::allocate/int"); - ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_FLOAT, 0f), "NativeAllocator::allocate/float"); - ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_LONG, 0L), "NativeAllocator::allocate/long"); - ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_DOUBLE, 0d), "NativeAllocator::allocate/double"); - ScopedOperation.ofAllocator(a -> a.allocateArray(JAVA_BYTE, 1L), "NativeAllocator::allocateArray/size"); - ScopedOperation.ofAllocator(a -> a.allocateArray(JAVA_BYTE, new byte[]{0}), "NativeAllocator::allocateArray/byte"); - ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_CHAR, new char[]{0}), "NativeAllocator::allocateArray/char"); - ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_SHORT, new short[]{0}), "NativeAllocator::allocateArray/short"); - ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_INT, new int[]{0}), "NativeAllocator::allocateArray/int"); - ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_FLOAT, new float[]{0}), "NativeAllocator::allocateArray/float"); - ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_LONG, new long[]{0}), "NativeAllocator::allocateArray/long"); - ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_DOUBLE, new double[]{0}), "NativeAllocator::allocateArray/double"); + ScopedOperation.ofScope(a -> a.allocate(1), "Arena::allocate/size"); + ScopedOperation.ofScope(a -> a.allocate(1, 1), "Arena::allocate/size/align"); + ScopedOperation.ofScope(a -> a.allocate(JAVA_BYTE), "Arena::allocate/layout"); + ScopedOperation.ofScope(a -> a.allocate(JAVA_BYTE, (byte) 0), "Arena::allocate/byte"); + ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_CHAR, (char) 0), "Arena::allocate/char"); + ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_SHORT, (short) 0), "Arena::allocate/short"); + ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_INT, 0), "Arena::allocate/int"); + ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_FLOAT, 0f), "Arena::allocate/float"); + ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_LONG, 0L), "Arena::allocate/long"); + ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_DOUBLE, 0d), "Arena::allocate/double"); + ScopedOperation.ofScope(a -> a.allocateArray(JAVA_BYTE, 1L), "Arena::allocateArray/size"); + ScopedOperation.ofScope(a -> a.allocateArray(JAVA_BYTE, new byte[]{0}), "Arena::allocateArray/byte"); + ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_CHAR, new char[]{0}), "Arena::allocateArray/char"); + ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_SHORT, new short[]{0}), "Arena::allocateArray/short"); + ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_INT, new int[]{0}), "Arena::allocateArray/int"); + ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_FLOAT, new float[]{0}), "Arena::allocateArray/float"); + ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_LONG, new long[]{0}), "Arena::allocateArray/long"); + ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_DOUBLE, new double[]{0}), "Arena::allocateArray/double"); }; @DataProvider(name = "scopedOperations") @@ -161,13 +144,13 @@ public class TestScopedOperations { return scopedOperations.stream().map(op -> new Object[] { op.name, op }).toArray(Object[][]::new); } - static class ScopedOperation implements Consumer, Function { + static class ScopedOperation implements Consumer, Function { - final Function factory; + final Function factory; final Consumer operation; final String name; - private ScopedOperation(Function factory, Consumer operation, String name) { + private ScopedOperation(Function factory, Consumer operation, String name) { this.factory = factory; this.operation = operation; this.name = name; @@ -179,23 +162,18 @@ public class TestScopedOperations { } @Override - public X apply(SegmentScope session) { + public X apply(Arena session) { return factory.apply(session); } - static void of(Function factory, Consumer consumer, String name) { + static void of(Function factory, Consumer consumer, String name) { scopedOperations.add(new ScopedOperation<>(factory, consumer, name)); } - static void ofScope(Consumer scopeConsumer, String name) { + static void ofScope(Consumer scopeConsumer, String name) { scopedOperations.add(new ScopedOperation<>(Function.identity(), scopeConsumer, name)); } - static void ofVaList(Consumer vaListConsumer, String name) { - scopedOperations.add(new ScopedOperation<>(session -> VaList.make(builder -> builder.addVarg(JAVA_LONG, 42), session), - vaListConsumer, name)); - } - static void ofSegment(Consumer segmentConsumer, String name) { for (SegmentFactory segmentFactory : SegmentFactory.values()) { scopedOperations.add(new ScopedOperation<>( @@ -205,18 +183,9 @@ public class TestScopedOperations { } } - static void ofAllocator(Consumer allocatorConsumer, String name) { - for (AllocatorFactory allocatorFactory : AllocatorFactory.values()) { - scopedOperations.add(new ScopedOperation<>( - allocatorFactory.allocatorFactory, - allocatorConsumer, - allocatorFactory.name() + "/" + name)); - } - } - enum SegmentFactory { - NATIVE(session -> MemorySegment.allocateNative(10, session)), + NATIVE(session -> session.allocate(10, 1)), MAPPED(session -> { try (FileChannel fileChannel = FileChannel.open(Path.of("foo.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE)) { return fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 10L, session); @@ -224,7 +193,7 @@ public class TestScopedOperations { throw new AssertionError(ex); } }), - UNSAFE(session -> MemorySegment.ofAddress(0, 10, session)); + UNSAFE(session -> MemorySegment.NULL.reinterpret(10, session, null)); static { try { @@ -236,21 +205,11 @@ public class TestScopedOperations { } } - final Function segmentFactory; + final Function segmentFactory; - SegmentFactory(Function segmentFactory) { + SegmentFactory(Function segmentFactory) { this.segmentFactory = segmentFactory; } } - - enum AllocatorFactory { - NATIVE_ALLOCATOR(SegmentAllocator::nativeAllocator); - - final Function allocatorFactory; - - AllocatorFactory(Function allocatorFactory) { - this.allocatorFactory = allocatorFactory; - } - } } } diff --git a/test/jdk/java/foreign/TestSegmentAllocators.java b/test/jdk/java/foreign/TestSegmentAllocators.java index 94dd265563e..2d8fad428fb 100644 --- a/test/jdk/java/foreign/TestSegmentAllocators.java +++ b/test/jdk/java/foreign/TestSegmentAllocators.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,14 +25,17 @@ /* * @test * @enablePreview + * @modules java.base/jdk.internal.foreign * @run testng/othervm TestSegmentAllocators */ import java.lang.foreign.*; +import jdk.internal.foreign.MappedMemorySegmentImpl; +import jdk.internal.foreign.NativeMemorySegmentImpl; import org.testng.annotations.*; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -69,8 +72,8 @@ public class TestSegmentAllocators { List addressList = new ArrayList<>(); int elems = ELEMS / ((int)alignedLayout.byteAlignment() / (int)layout.byteAlignment()); Arena[] arenas = { - Arena.openConfined(), - Arena.openShared() + Arena.ofConfined(), + Arena.ofShared() }; for (Arena arena : arenas) { try (arena) { @@ -103,7 +106,7 @@ public class TestSegmentAllocators { @Test public void testBigAllocationInUnboundedSession() { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { for (int i = 8 ; i < SIZE_256M ; i *= 8) { SegmentAllocator allocator = SegmentAllocator.slicingAllocator(arena.allocate(i * 2 + 1)); MemorySegment address = allocator.allocate(i, i); @@ -117,7 +120,7 @@ public class TestSegmentAllocators { @Test public void testTooBigForBoundedArena() { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { SegmentAllocator allocator = SegmentAllocator.slicingAllocator(arena.allocate(10)); assertThrows(IndexOutOfBoundsException.class, () -> allocator.allocate(12)); allocator.allocate(5); @@ -151,7 +154,7 @@ public class TestSegmentAllocators { @Test(expectedExceptions = OutOfMemoryError.class) public void testBadArenaNullReturn() { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { arena.allocate(Long.MAX_VALUE, 2); } } @@ -188,7 +191,7 @@ public class TestSegmentAllocators { @Override public MemorySegment allocate(long byteSize, long byteAlignment) { - return MemorySegment.allocateNative(byteSize, byteAlignment, SegmentScope.auto()); + return Arena.ofAuto().allocate(byteSize, byteAlignment); } @Override @@ -206,8 +209,8 @@ public class TestSegmentAllocators { public void testArray(AllocationFactory allocationFactory, ValueLayout layout, AllocationFunction allocationFunction, ToArrayHelper arrayHelper) { Z arr = arrayHelper.array(); Arena[] arenas = { - Arena.openConfined(), - Arena.openShared() + Arena.ofConfined(), + Arena.ofShared() }; for (Arena arena : arenas) { try (arena) { @@ -219,6 +222,27 @@ public class TestSegmentAllocators { } } + @Test(dataProvider = "arrayAllocations") + public void testPredicatesAndCommands(AllocationFactory allocationFactory, ValueLayout layout, AllocationFunction allocationFunction, ToArrayHelper arrayHelper) { + Z arr = arrayHelper.array(); + Arena[] arenas = { + Arena.ofConfined(), + Arena.ofShared() + }; + for (Arena arena : arenas) { + try (arena) { + SegmentAllocator allocator = allocationFactory.allocator(100, arena); + MemorySegment segment = allocationFunction.allocate(allocator, layout, arr); + assertThrows(UnsupportedOperationException.class, segment::load); + assertThrows(UnsupportedOperationException.class, segment::unload); + assertThrows(UnsupportedOperationException.class, segment::isLoaded); + assertThrows(UnsupportedOperationException.class, segment::force); + assertFalse(segment.isMapped()); + assertEquals(segment.isNative(), segment instanceof NativeMemorySegmentImpl); + } + } + } + @DataProvider(name = "scalarAllocations") static Object[][] scalarAllocations() { List scalarAllocations = new ArrayList<>(); @@ -336,7 +360,7 @@ public class TestSegmentAllocators { interface OfFloat extends AllocationFunction { } interface OfLong extends AllocationFunction { } interface OfDouble extends AllocationFunction { } - interface OfAddress extends AllocationFunction { } + interface OfAddress extends AllocationFunction { } interface OfByteArray extends AllocationFunction { } interface OfCharArray extends AllocationFunction { } @@ -348,8 +372,9 @@ public class TestSegmentAllocators { } enum AllocationFactory { - SLICING(true, (size, drop) -> SegmentAllocator.slicingAllocator(MemorySegment.allocateNative(size, drop.scope()))), - NATIVE_ALLOCATOR(false, (size, drop) -> SegmentAllocator.nativeAllocator(drop.scope())); + SLICING(true, (size, arena) -> { + return SegmentAllocator.slicingAllocator(arena.allocate(size, 1)); + }); private final boolean isBound; private final BiFunction factory; @@ -481,8 +506,7 @@ public class TestSegmentAllocators { @DataProvider(name = "allocators") static Object[][] allocators() { return new Object[][] { - { SegmentAllocator.nativeAllocator(SegmentScope.global()) }, - { SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(10, SegmentScope.global())) }, + { SegmentAllocator.prefixAllocator(Arena.global().allocate(10, 1)) }, }; } } diff --git a/test/jdk/java/foreign/TestSegmentCopy.java b/test/jdk/java/foreign/TestSegmentCopy.java index c82d4e5ded8..8dccd11bd4f 100644 --- a/test/jdk/java/foreign/TestSegmentCopy.java +++ b/test/jdk/java/foreign/TestSegmentCopy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -28,8 +28,8 @@ * @run testng TestSegmentCopy */ +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -144,7 +144,7 @@ public class TestSegmentCopy { static class SegmentSlice { enum Kind { - NATIVE(i -> MemorySegment.allocateNative(i, SegmentScope.auto())), + NATIVE(i -> Arena.ofAuto().allocate(i, 1)), ARRAY(i -> MemorySegment.ofArray(new byte[i])); final IntFunction segmentFactory; diff --git a/test/jdk/java/foreign/TestSegmentOffset.java b/test/jdk/java/foreign/TestSegmentOffset.java index f3adcb67c89..9f61f1c3511 100644 --- a/test/jdk/java/foreign/TestSegmentOffset.java +++ b/test/jdk/java/foreign/TestSegmentOffset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -27,12 +27,12 @@ * @run testng TestSegmentOffset */ +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.lang.foreign.SegmentScope; import java.util.ArrayList; import java.util.List; import java.util.function.IntFunction; @@ -81,7 +81,7 @@ public class TestSegmentOffset { static class SegmentSlice { enum Kind { - NATIVE(i -> MemorySegment.allocateNative(i, SegmentScope.auto())), + NATIVE(i -> Arena.ofAuto().allocate(i, 1)), ARRAY(i -> MemorySegment.ofArray(new byte[i])); final IntFunction segmentFactory; diff --git a/test/jdk/java/foreign/TestSegmentOverlap.java b/test/jdk/java/foreign/TestSegmentOverlap.java index bb0e07da817..8832ffd1a46 100644 --- a/test/jdk/java/foreign/TestSegmentOverlap.java +++ b/test/jdk/java/foreign/TestSegmentOverlap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -29,7 +29,7 @@ import java.io.File; import java.io.IOException; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; @@ -62,10 +62,10 @@ public class TestSegmentOverlap { @DataProvider(name = "segmentFactories") public Object[][] segmentFactories() { List> l = List.of( - () -> MemorySegment.allocateNative(16, SegmentScope.auto()), + () -> Arena.ofAuto().allocate(16, 1), () -> { try (FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - return fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 16L, SegmentScope.auto()); + return fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 16L, Arena.ofAuto()); } catch (IOException e) { throw new RuntimeException(e); } @@ -132,7 +132,7 @@ public class TestSegmentOverlap { } enum OtherSegmentFactory { - NATIVE(() -> MemorySegment.allocateNative(16, SegmentScope.auto())), + NATIVE(() -> Arena.ofAuto().allocate(16, 1)), HEAP(() -> MemorySegment.ofArray(new byte[]{16})); final Supplier factory; diff --git a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java index a721b35fca4..ed8aba5e61a 100644 --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -28,11 +28,8 @@ * @run testng/othervm -Xmx4G -XX:MaxDirectMemorySize=1M --enable-native-access=ALL-UNNAMED TestSegments */ -import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -50,23 +47,23 @@ public class TestSegments { @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class) public void testBadAllocateAlign(long size, long align) { - MemorySegment.allocateNative(size, align, SegmentScope.auto()); + Arena.ofAuto().allocate(size, align); } @Test public void testZeroLengthNativeSegment() { - try (Arena arena = Arena.openConfined()) { - SegmentScope session = arena.scope(); - var segment = MemorySegment.allocateNative(0, session); + try (Arena arena = Arena.ofConfined()) { + Arena session = arena; + var segment = session.allocate(0, 1); assertEquals(segment.byteSize(), 0); MemoryLayout seq = MemoryLayout.sequenceLayout(0, JAVA_INT); - segment = MemorySegment.allocateNative(seq, session); + segment = session.allocate(seq); assertEquals(segment.byteSize(), 0); assertEquals(segment.address() % seq.byteAlignment(), 0); - segment = MemorySegment.allocateNative(0, 4, session); + segment = session.allocate(0, 4); assertEquals(segment.byteSize(), 0); assertEquals(segment.address() % 4, 0); - MemorySegment rawAddress = MemorySegment.ofAddress(segment.address(), 0, session); + MemorySegment rawAddress = MemorySegment.ofAddress(segment.address()); assertEquals(rawAddress.byteSize(), 0); assertEquals(rawAddress.address() % 4, 0); } @@ -75,19 +72,20 @@ public class TestSegments { @Test(expectedExceptions = { OutOfMemoryError.class, IllegalArgumentException.class }) public void testAllocateTooBig() { - MemorySegment.allocateNative(Long.MAX_VALUE, SegmentScope.auto()); + Arena.ofAuto().allocate(Long.MAX_VALUE, 1); } @Test(expectedExceptions = OutOfMemoryError.class) public void testNativeAllocationTooBig() { - MemorySegment segment = MemorySegment.allocateNative(1024L * 1024 * 8 * 2, SegmentScope.auto()); // 2M + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(1024L * 1024 * 8 * 2, 1); // 2M } @Test public void testNativeSegmentIsZeroed() { VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(1000, 1, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(1000, 1); for (long i = 0 ; i < segment.byteSize() ; i++) { assertEquals(0, (byte)byteHandle.get(segment, i)); } @@ -97,8 +95,8 @@ public class TestSegments { @Test public void testSlices() { VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(10, 1, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(10, 1); //init for (byte i = 0 ; i < segment.byteSize() ; i++) { byteHandle.set(segment, (long)i, i); @@ -115,16 +113,32 @@ public class TestSegments { } } + @Test(dataProvider = "segmentFactories") + public void testDerivedScopes(Supplier segmentSupplier) { + MemorySegment segment = segmentSupplier.get(); + assertEquals(segment.scope(), segment.scope()); + // one level + assertEquals(segment.asSlice(0).scope(), segment.scope()); + assertEquals(segment.asReadOnly().scope(), segment.scope()); + // two levels + assertEquals(segment.asSlice(0).asReadOnly().scope(), segment.scope()); + assertEquals(segment.asReadOnly().asSlice(0).scope(), segment.scope()); + // check fresh every time + MemorySegment another = segmentSupplier.get(); + assertNotEquals(segment.scope(), another.scope()); + } + @Test public void testEqualsOffHeap() { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + Arena scope1 = arena; + MemorySegment segment = scope1.allocate(100, 1); assertEquals(segment, segment.asReadOnly()); assertEquals(segment, segment.asSlice(0, 100)); assertNotEquals(segment, segment.asSlice(10, 90)); assertEquals(segment, segment.asSlice(0, 90)); - assertEquals(segment, MemorySegment.ofAddress(segment.address(), 100, SegmentScope.global())); - MemorySegment segment2 = MemorySegment.allocateNative(100, arena.scope()); + assertEquals(segment, MemorySegment.ofAddress(segment.address())); + MemorySegment segment2 = arena.allocate(100, 1); assertNotEquals(segment, segment2); } } @@ -142,12 +156,12 @@ public class TestSegments { @Test public void testHashCodeOffHeap() { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(100, 1); assertEquals(segment.hashCode(), segment.asReadOnly().hashCode()); assertEquals(segment.hashCode(), segment.asSlice(0, 100).hashCode()); assertEquals(segment.hashCode(), segment.asSlice(0, 90).hashCode()); - assertEquals(segment.hashCode(), MemorySegment.ofAddress(segment.address(), 100, SegmentScope.global()).hashCode()); + assertEquals(segment.hashCode(), MemorySegment.ofAddress(segment.address()).hashCode()); } } @@ -162,21 +176,23 @@ public class TestSegments { @Test(expectedExceptions = IndexOutOfBoundsException.class) public void testSmallSegmentMax() { long offset = (long)Integer.MAX_VALUE + (long)Integer.MAX_VALUE + 2L + 6L; // overflows to 6 when cast to int - MemorySegment memorySegment = MemorySegment.allocateNative(10, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment memorySegment = scope.allocate(10, 1); memorySegment.get(JAVA_INT, offset); } @Test(expectedExceptions = IndexOutOfBoundsException.class) public void testSmallSegmentMin() { long offset = ((long)Integer.MIN_VALUE * 2L) + 6L; // underflows to 6 when cast to int - MemorySegment memorySegment = MemorySegment.allocateNative(10L, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment memorySegment = scope.allocate(10L, 1); memorySegment.get(JAVA_INT, offset); } @Test public void testSegmentOOBMessage() { try { - var segment = MemorySegment.allocateNative(10, SegmentScope.global()); + var segment = Arena.global().allocate(10, 1); segment.getAtIndex(ValueLayout.JAVA_INT, 2); } catch (IndexOutOfBoundsException ex) { assertTrue(ex.getMessage().contains("Out of bound access")); @@ -194,20 +210,18 @@ public class TestSegments { @DataProvider(name = "scopes") public Object[][] scopes() { return new Object[][] { - { SegmentScope.auto(), false }, - { SegmentScope.global(), false }, - { Arena.openConfined().scope(), true }, - { Arena.openShared().scope(), false } + { Arena.ofAuto(), false }, + { Arena.global(), false }, + { Arena.ofConfined(), true }, + { Arena.ofShared(), false } }; } @Test(dataProvider = "scopes") - public void testIsAccessibleBy(SegmentScope scope, boolean isConfined) { - assertTrue(scope.isAccessibleBy(Thread.currentThread())); - assertTrue(scope.isAccessibleBy(new Thread()) != isConfined); - MemorySegment segment = MemorySegment.ofAddress(0, 0, scope); - assertTrue(segment.scope().isAccessibleBy(Thread.currentThread())); - assertTrue(segment.scope().isAccessibleBy(new Thread()) != isConfined); + public void testIsAccessibleBy(Arena arena, boolean isConfined) { + MemorySegment segment = MemorySegment.NULL.reinterpret(arena, null); + assertTrue(segment.isAccessibleBy(Thread.currentThread())); + assertTrue(segment.isAccessibleBy(new Thread()) != isConfined); } @DataProvider(name = "segmentFactories") @@ -220,12 +234,12 @@ public class TestSegments { () -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }), () -> MemorySegment.ofArray(new long[] { 1l, 2l, 3l, 4l } ), () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ), - () -> MemorySegment.allocateNative(4L, SegmentScope.auto()), - () -> MemorySegment.allocateNative(4L, 8, SegmentScope.auto()), - () -> MemorySegment.allocateNative(JAVA_INT, SegmentScope.auto()), - () -> MemorySegment.allocateNative(4L, SegmentScope.auto()), - () -> MemorySegment.allocateNative(4L, 8, SegmentScope.auto()), - () -> MemorySegment.allocateNative(JAVA_INT, SegmentScope.auto()) + () -> Arena.ofAuto().allocate(4L, 1), + () -> Arena.ofAuto().allocate(4L, 8), + () -> Arena.ofAuto().allocate(JAVA_INT), + () -> Arena.ofAuto().allocate(4L, 1), + () -> Arena.ofAuto().allocate(4L, 8), + () -> Arena.ofAuto().allocate(JAVA_INT) ); return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); @@ -257,9 +271,41 @@ public class TestSegments { } @Test(dataProvider = "segmentFactories") - public void testNativeSegments(Supplier segmentSupplier) { + public void testHeapBase(Supplier segmentSupplier) { MemorySegment segment = segmentSupplier.get(); - assertEquals(segment.isNative(), !segment.array().isPresent()); + assertEquals(segment.isNative(), !segment.heapBase().isPresent()); + segment = segment.asReadOnly(); + assertTrue(segment.heapBase().isEmpty()); + } + + @Test + public void testScopeConfinedArena() { + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(100); + assertEquals(segment.scope(), arena.scope()); + } + } + + @Test + public void testScopeSharedArena() { + try (Arena arena = Arena.ofShared()) { + MemorySegment segment = arena.allocate(100); + assertEquals(segment.scope(), arena.scope()); + } + } + + @Test + public void testScopeAutoArena() { + Arena arena = Arena.ofAuto(); + MemorySegment segment = arena.allocate(100); + assertEquals(segment.scope(), arena.scope()); + } + + @Test + public void testScopeGlobalArena() { + Arena arena = Arena.global(); + MemorySegment segment = arena.allocate(100); + assertEquals(segment.scope(), arena.scope()); } @Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class) @@ -283,7 +329,7 @@ public class TestSegments { thread.start(); thread.join(); - if (!segment.scope().isAccessibleBy(Thread.currentThread())) { + if (!segment.isAccessibleBy(Thread.currentThread())) { RuntimeException e = exception.get(); throw e; } else { diff --git a/test/jdk/java/foreign/TestSharedAccess.java b/test/jdk/java/foreign/TestSharedAccess.java index 5c88b35a078..62f20c6be58 100644 --- a/test/jdk/java/foreign/TestSharedAccess.java +++ b/test/jdk/java/foreign/TestSharedAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -49,8 +49,8 @@ public class TestSharedAccess { @Test public void testShared() throws Throwable { SequenceLayout layout = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); - try (Arena arena = Arena.openShared()) { - MemorySegment s = MemorySegment.allocateNative(layout, arena.scope());; + try (Arena arena = Arena.ofShared()) { + MemorySegment s = arena.allocate(layout);; for (int i = 0 ; i < layout.elementCount() ; i++) { setInt(s.asSlice(i * 4), 42); } @@ -94,15 +94,14 @@ public class TestSharedAccess { @Test public void testSharedUnsafe() throws Throwable { - try (Arena arena = Arena.openShared()) { - MemorySegment s = MemorySegment.allocateNative(4, 1, arena.scope());; + try (Arena arena = Arena.ofShared()) { + MemorySegment s = arena.allocate(4, 1);; setInt(s, 42); assertEquals(getInt(s), 42); List threads = new ArrayList<>(); - MemorySegment sharedSegment = MemorySegment.ofAddress(s.address(), s.byteSize(), arena.scope()); for (int i = 0 ; i < 1000 ; i++) { threads.add(new Thread(() -> { - assertEquals(getInt(sharedSegment), 42); + assertEquals(getInt(s), 42); })); } threads.forEach(Thread::start); @@ -121,8 +120,9 @@ public class TestSharedAccess { CountDownLatch a = new CountDownLatch(1); CountDownLatch b = new CountDownLatch(1); CompletableFuture r; - try (Arena arena = Arena.openConfined()) { - MemorySegment s1 = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT), arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemoryLayout layout = MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT); + MemorySegment s1 = arena.allocate(layout);; r = CompletableFuture.runAsync(() -> { try { ByteBuffer bb = s1.asByteBuffer(); diff --git a/test/jdk/java/foreign/TestSlices.java b/test/jdk/java/foreign/TestSlices.java index 9cceaee9b75..988d4423b2c 100644 --- a/test/jdk/java/foreign/TestSlices.java +++ b/test/jdk/java/foreign/TestSlices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -27,6 +27,8 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.List; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -46,8 +48,8 @@ public class TestSlices { @Test(dataProvider = "slices") public void testSlices(VarHandle handle, int lo, int hi, int[] values) { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(LAYOUT, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(LAYOUT);; //init for (long i = 0 ; i < 2 ; i++) { for (long j = 0 ; j < 5 ; j++) { @@ -61,8 +63,8 @@ public class TestSlices { @Test(dataProvider = "slices") public void testSliceBadIndex(VarHandle handle, int lo, int hi, int[] values) { - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(LAYOUT, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(LAYOUT);; assertThrows(() -> handle.get(segment, lo, 0)); assertThrows(() -> handle.get(segment, 0, hi)); } @@ -79,6 +81,59 @@ public class TestSlices { assertEquals(index, values.length); } + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceNegativeOffset() { + MemorySegment.ofArray(new byte[100]).asSlice(-1); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceNegativeOffsetGoodSize() { + MemorySegment.ofArray(new byte[100]).asSlice(-1, 10); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceGoodOffsetNegativeSize() { + MemorySegment.ofArray(new byte[100]).asSlice(10, -1); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceNegativeOffsetGoodLayout() { + MemorySegment.ofArray(new byte[100]).asSlice(-1, ValueLayout.JAVA_INT); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceOffsetTooBig() { + MemorySegment.ofArray(new byte[100]).asSlice(120); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceOffsetTooBigSizeGood() { + MemorySegment.ofArray(new byte[100]).asSlice(120, 0); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceOffsetOkSizeTooBig() { + MemorySegment.ofArray(new byte[100]).asSlice(0, 120); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class) + public void testSliceLayoutTooBig() { + MemorySegment.ofArray(new byte[100]) + .asSlice(0, MemoryLayout.sequenceLayout(120, ValueLayout.JAVA_BYTE)); + } + + @Test(dataProvider = "segmentsAndLayouts") + public void testSliceAlignment(MemorySegment segment, long alignment, ValueLayout layout) { + boolean badAlign = layout.byteAlignment() > alignment; + try { + segment.asSlice(0, layout); + assertFalse(badAlign); + } catch (IllegalArgumentException ex) { + assertTrue(badAlign); + assertTrue(ex.getMessage().contains("incompatible with alignment constraints")); + } + } + @DataProvider(name = "slices") static Object[][] slices() { return new Object[][] { @@ -98,4 +153,56 @@ public class TestSlices { MemoryLayout.PathElement.sequenceElement(3, -2)), 2, 2, new int[] { 4, 2, 9, 7 } }, }; } + + @DataProvider(name = "segmentsAndLayouts") + static Object[][] segmentsAndLayouts() { + List segmentsAndLayouts = new ArrayList<>(); + for (SegmentKind sk : SegmentKind.values()) { + for (LayoutKind lk : LayoutKind.values()) { + for (int align : new int[]{ 1, 2, 4, 8 }) { + if (align > sk.maxAlign) break; + segmentsAndLayouts.add(new Object[] { sk.segment.asSlice(align), align, lk.layout }); + } + } + } + return segmentsAndLayouts.toArray(Object[][]::new); + } + + enum SegmentKind { + NATIVE(Arena.ofAuto().allocate(100), 8), + BYTE_ARRAY(MemorySegment.ofArray(new byte[100]), 1), + CHAR_ARRAY(MemorySegment.ofArray(new char[100]), 2), + SHORT_ARRAY(MemorySegment.ofArray(new short[100]), 2), + INT_ARRAY(MemorySegment.ofArray(new int[100]), 4), + FLOAT_ARRAY(MemorySegment.ofArray(new float[100]), 4), + LONG_ARRAY(MemorySegment.ofArray(new long[100]), 8), + DOUBLE_ARRAY(MemorySegment.ofArray(new double[100]), 8); + + + final MemorySegment segment; + final int maxAlign; + + SegmentKind(MemorySegment segment, int maxAlign) { + this.segment = segment; + this.maxAlign = maxAlign; + } + } + + enum LayoutKind { + BOOL(ValueLayout.JAVA_BOOLEAN), + CHAR(ValueLayout.JAVA_CHAR), + SHORT(ValueLayout.JAVA_SHORT), + INT(ValueLayout.JAVA_INT), + FLOAT(ValueLayout.JAVA_FLOAT), + LONG(ValueLayout.JAVA_LONG), + DOUBLE(ValueLayout.JAVA_DOUBLE), + ADDRESS(ValueLayout.ADDRESS); + + + final ValueLayout layout; + + LayoutKind(ValueLayout segment) { + this.layout = segment; + } + } } diff --git a/test/jdk/java/foreign/TestSpliterator.java b/test/jdk/java/foreign/TestSpliterator.java index 574c184522f..5cbe29f52dd 100644 --- a/test/jdk/java/foreign/TestSpliterator.java +++ b/test/jdk/java/foreign/TestSpliterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -27,11 +27,7 @@ * @run testng TestSpliterator */ -import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SequenceLayout; +import java.lang.foreign.*; import java.lang.invoke.VarHandle; import java.util.LinkedList; @@ -42,8 +38,6 @@ import java.util.concurrent.RecursiveTask; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.LongStream; -import java.lang.foreign.ValueLayout; - import org.testng.annotations.*; import static org.testng.Assert.*; @@ -59,8 +53,8 @@ public class TestSpliterator { SequenceLayout layout = MemoryLayout.sequenceLayout(size, ValueLayout.JAVA_INT); //setup - try (Arena arena = Arena.openShared()) { - MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());; + try (Arena arena = Arena.ofShared()) { + MemorySegment segment = arena.allocate(layout);; for (int i = 0; i < layout.elementCount(); i++) { INT_HANDLE.set(segment, (long) i, i); } @@ -86,7 +80,8 @@ public class TestSpliterator { SequenceLayout layout = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); //setup - MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(layout); for (int i = 0; i < layout.elementCount(); i++) { INT_HANDLE.set(segment, (long) i, i); } @@ -101,55 +96,64 @@ public class TestSpliterator { @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeTooBig() { - MemorySegment.allocateNative(2, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(2, 1) .spliterator(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeTooBig() { - MemorySegment.allocateNative(2, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(2, 1) .elements(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeNotMultiple() { - MemorySegment.allocateNative(7, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(7, 1) .spliterator(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeNotMultiple() { - MemorySegment.allocateNative(7, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(7, 1) .elements(ValueLayout.JAVA_INT); } @Test public void testSpliteratorElementSizeMultipleButNotPowerOfTwo() { - MemorySegment.allocateNative(12, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(12, 1) .spliterator(ValueLayout.JAVA_INT); } @Test public void testStreamElementSizeMultipleButNotPowerOfTwo() { - MemorySegment.allocateNative(12, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(12, 1) .elements(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeZero() { - MemorySegment.allocateNative(7, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(7, 1) .spliterator(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeZero() { - MemorySegment.allocateNative(7, SegmentScope.auto()) + Arena scope = Arena.ofAuto(); + scope.allocate(7, 1) .elements(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); } @Test(expectedExceptions = IllegalArgumentException.class) public void testHyperAligned() { - MemorySegment segment = MemorySegment.allocateNative(8, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(8, 1); // compute an alignment constraint (in bytes) which exceed that of the native segment long bigByteAlign = Long.lowestOneBit(segment.address()) << 1; segment.elements(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT.withBitAlignment(bigByteAlign * 8))); diff --git a/test/jdk/java/foreign/TestStringEncoding.java b/test/jdk/java/foreign/TestStringEncoding.java index 5e41c0a7d06..caf1a0b044b 100644 --- a/test/jdk/java/foreign/TestStringEncoding.java +++ b/test/jdk/java/foreign/TestStringEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -39,7 +39,7 @@ public class TestStringEncoding { @Test(dataProvider = "strings") public void testStrings(String testString, int expectedByteLength) { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MemorySegment text = arena.allocateUtf8String(testString); assertEquals(text.byteSize(), expectedByteLength); diff --git a/test/jdk/java/foreign/TestTypeAccess.java b/test/jdk/java/foreign/TestTypeAccess.java index e84af14aa05..677c0d3abac 100644 --- a/test/jdk/java/foreign/TestTypeAccess.java +++ b/test/jdk/java/foreign/TestTypeAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -53,32 +53,32 @@ public class TestTypeAccess { @Test(expectedExceptions=ClassCastException.class) public void testMemoryAddressValueGetAsString() { - try (Arena arena = Arena.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment s = arena.allocate(8, 8);; String address = (String)ADDR_HANDLE.get(s); } } @Test(expectedExceptions=ClassCastException.class) public void testMemoryAddressValueSetAsString() { - try (Arena arena = Arena.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment s = arena.allocate(8, 8);; ADDR_HANDLE.set(s, "string"); } } @Test(expectedExceptions=WrongMethodTypeException.class) public void testMemoryAddressValueGetAsPrimitive() { - try (Arena arena = Arena.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment s = arena.allocate(8, 8);; int address = (int)ADDR_HANDLE.get(s); } } @Test(expectedExceptions=WrongMethodTypeException.class) public void testMemoryAddressValueSetAsPrimitive() { - try (Arena arena = Arena.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment s = arena.allocate(8, 8);; ADDR_HANDLE.set(s, 1); } } diff --git a/test/jdk/java/foreign/TestUnsupportedLinker.java b/test/jdk/java/foreign/TestUnsupportedLinker.java index 750ff0fd00e..7ebd83a8a99 100644 --- a/test/jdk/java/foreign/TestUnsupportedLinker.java +++ b/test/jdk/java/foreign/TestUnsupportedLinker.java @@ -24,14 +24,11 @@ /* * @test * @enablePreview - * @summary test with unknown architecture override - * @run testng/othervm -Dos.arch=unknown --enable-native-access=ALL-UNNAMED TestUnsupportedLinker + * + * @run testng/othervm -Djdk.internal.foreign.CABI=UNSUPPORTED --enable-native-access=ALL-UNNAMED TestUnsupportedLinker */ import java.lang.foreign.Linker; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.VaList; -import java.lang.foreign.ValueLayout; import org.testng.annotations.Test; @@ -41,19 +38,4 @@ public class TestUnsupportedLinker { public void testLinker() { Linker.nativeLinker(); } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testEmptyVaList() { - VaList.empty(); - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testNonEmptyVaList() { - VaList.make(builder -> builder.addVarg(ValueLayout.JAVA_INT, 42), SegmentScope.auto()); - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testUnsafeVaList() { - VaList.ofAddress(0L, SegmentScope.auto()); - } } diff --git a/test/jdk/java/foreign/TestUpcallAsync.java b/test/jdk/java/foreign/TestUpcallAsync.java index f07902dbbc9..912bd63e702 100644 --- a/test/jdk/java/foreign/TestUpcallAsync.java +++ b/test/jdk/java/foreign/TestUpcallAsync.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -59,7 +59,7 @@ public class TestUpcallAsync extends TestUpcallBase { List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); MemorySegment addr = findNativeOrThrow(fName); - try (Arena arena = Arena.openShared()) { + try (Arena arena = Arena.ofShared()) { FunctionDescriptor descriptor = function(ret, paramTypes, fields); MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor); AtomicReference capturedArgs = new AtomicReference<>(); @@ -70,7 +70,7 @@ public class TestUpcallAsync extends TestUpcallBase { FunctionDescriptor callbackDesc = descriptor.returnLayout() .map(FunctionDescriptor::of) .orElse(FunctionDescriptor.ofVoid()); - MemorySegment callback = LINKER.upcallStub(mh, callbackDesc, arena.scope()); + MemorySegment callback = LINKER.upcallStub(mh, callbackDesc, arena); MethodHandle invoker = asyncInvoker(ret, ret == Ret.VOID ? null : paramTypes.get(0), fields); diff --git a/test/jdk/java/foreign/TestUpcallException.java b/test/jdk/java/foreign/TestUpcallException.java index e783e36f94f..2f68b15fda2 100644 --- a/test/jdk/java/foreign/TestUpcallException.java +++ b/test/jdk/java/foreign/TestUpcallException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -26,7 +26,7 @@ * @enablePreview * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64" * @library /test/lib - * @build ThrowingUpcall TestUpcallException + * @build TestUpcallException * * @run testng/othervm/native * --enable-native-access=ALL-UNNAMED @@ -37,22 +37,88 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.IOException; +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; public class TestUpcallException extends UpcallTestHelper { - @Test(dataProvider = "cases") - public void testException(boolean useSpec, boolean isVoid) throws InterruptedException, IOException { - runInNewProcess(ThrowingUpcall.class, useSpec, isVoid ? "void" : "") + @Test(dataProvider = "exceptionCases") + public void testException(Class target, boolean useSpec) throws InterruptedException, IOException { + runInNewProcess(target, useSpec) .assertStdErrContains("Testing upcall exceptions"); } @DataProvider - public static Object[][] cases() { + public static Object[][] exceptionCases() { return new Object[][]{ - { false, true, }, - { false, false, }, - { true, true, }, - { true, false, } + { VoidUpcallRunner.class, false }, + { NonVoidUpcallRunner.class, false }, + { VoidUpcallRunner.class, true }, + { NonVoidUpcallRunner.class, true } }; } + + public static class VoidUpcallRunner extends ExceptionRunnerBase { + public static void main(String[] args) throws Throwable { + try (Arena arena = Arena.ofConfined()) { + MemorySegment stub = Linker.nativeLinker().upcallStub(VOID_TARGET, FunctionDescriptor.ofVoid(), arena); + downcallVoid.invoke(stub); // should call Shutdown.exit(1); + } + } + } + + public static class NonVoidUpcallRunner extends ExceptionRunnerBase { + public static void main(String[] args) throws Throwable { + try (Arena arena = Arena.ofConfined()) { + MemorySegment stub = Linker.nativeLinker().upcallStub(INT_TARGET, FunctionDescriptor.of(C_INT, C_INT), arena); + downcallNonVoid.invoke(42, stub); // should call Shutdown.exit(1); + } + } + } + + // where + + private static class ExceptionRunnerBase { + static final MethodHandle downcallVoid; + static final MethodHandle downcallNonVoid; + static final MethodHandle VOID_TARGET; + static final MethodHandle INT_TARGET; + + static final Thread.UncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER + = (thread, throwable) -> System.out.println("From uncaught exception handler"); + + static { + System.loadLibrary("TestUpcall"); + downcallVoid = Linker.nativeLinker().downcallHandle( + findNativeOrThrow("f0_V__"), + FunctionDescriptor.ofVoid(C_POINTER) + ); + downcallNonVoid = Linker.nativeLinker().downcallHandle( + findNativeOrThrow("f10_I_I_"), + FunctionDescriptor.of(C_INT, C_INT, C_POINTER) + ); + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + VOID_TARGET = lookup.findStatic(ExceptionRunnerBase.class, "throwException", + MethodType.methodType(void.class)); + INT_TARGET = lookup.findStatic(ExceptionRunnerBase.class, "throwException", + MethodType.methodType(int.class, int.class)); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + public static void throwException() { + throw new RuntimeException("Testing upcall exceptions"); + } + + public static int throwException(int x) { + throw new RuntimeException("Testing upcall exceptions"); + } + } } diff --git a/test/jdk/java/foreign/TestUpcallHighArity.java b/test/jdk/java/foreign/TestUpcallHighArity.java index fa7145c9985..f67ea737009 100644 --- a/test/jdk/java/foreign/TestUpcallHighArity.java +++ b/test/jdk/java/foreign/TestUpcallHighArity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -74,7 +74,7 @@ public class TestUpcallHighArity extends CallGeneratorHelper { public void testUpcall(MethodHandle downcall, MethodType upcallType, FunctionDescriptor upcallDescriptor) throws Throwable { AtomicReference capturedArgs = new AtomicReference<>(); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { Object[] args = new Object[upcallType.parameterCount() + 1]; args[0] = makeArgSaverCB(upcallDescriptor, arena, capturedArgs, -1); List argLayouts = upcallDescriptor.argumentLayouts(); diff --git a/test/jdk/java/foreign/TestUpcallScope.java b/test/jdk/java/foreign/TestUpcallScope.java index 8479115d735..777dc4f6d20 100644 --- a/test/jdk/java/foreign/TestUpcallScope.java +++ b/test/jdk/java/foreign/TestUpcallScope.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -56,7 +56,7 @@ public class TestUpcallScope extends TestUpcallBase { List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); MemorySegment addr = findNativeOrThrow(fName); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { FunctionDescriptor descriptor = function(ret, paramTypes, fields); MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor); AtomicReference capturedArgs = new AtomicReference<>(); diff --git a/test/jdk/java/foreign/TestUpcallStack.java b/test/jdk/java/foreign/TestUpcallStack.java index 4f6eb86abee..b32331ecfea 100644 --- a/test/jdk/java/foreign/TestUpcallStack.java +++ b/test/jdk/java/foreign/TestUpcallStack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -57,7 +57,7 @@ public class TestUpcallStack extends TestUpcallBase { List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); MemorySegment addr = findNativeOrThrow("s" + fName); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { FunctionDescriptor descriptor = functionStack(ret, paramTypes, fields); MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor); AtomicReference capturedArgs = new AtomicReference<>(); diff --git a/test/jdk/java/foreign/TestUpcallStructScope.java b/test/jdk/java/foreign/TestUpcallStructScope.java index ad315558523..fcb08b546cb 100644 --- a/test/jdk/java/foreign/TestUpcallStructScope.java +++ b/test/jdk/java/foreign/TestUpcallStructScope.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -29,19 +29,15 @@ * * @run testng/othervm/native * --enable-native-access=ALL-UNNAMED - * -Djdk.internal.foreign.ProgrammableInvoker.USE_SPEC=false + * -Djdk.internal.foreign.DowncallLinker.USE_SPEC=false * TestUpcallStructScope * @run testng/othervm/native * --enable-native-access=ALL-UNNAMED - * -Djdk.internal.foreign.ProgrammableInvoker.USE_SPEC=true + * -Djdk.internal.foreign.DowncallLinker.USE_SPEC=true * TestUpcallStructScope */ -import java.lang.foreign.Arena; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; +import java.lang.foreign.*; import org.testng.annotations.Test; @@ -89,9 +85,9 @@ public class TestUpcallStructScope extends NativeTestHelper { AtomicReference capturedSegment = new AtomicReference<>(); MethodHandle target = methodHandle(capturedSegment::set); FunctionDescriptor upcallDesc = FunctionDescriptor.ofVoid(S_PDI_LAYOUT); - try (Arena arena = Arena.openConfined()) { - MemorySegment upcallStub = LINKER.upcallStub(target, upcallDesc, arena.scope()); - MemorySegment argSegment = MemorySegment.allocateNative(S_PDI_LAYOUT, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment upcallStub = LINKER.upcallStub(target, upcallDesc, arena); + MemorySegment argSegment = arena.allocate(S_PDI_LAYOUT); MH_do_upcall.invoke(upcallStub, argSegment); } diff --git a/test/jdk/java/foreign/TestVarArgs.java b/test/jdk/java/foreign/TestVarArgs.java index dfa93f57abd..a93a34da716 100644 --- a/test/jdk/java/foreign/TestVarArgs.java +++ b/test/jdk/java/foreign/TestVarArgs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -39,8 +39,6 @@ import java.lang.foreign.MemorySegment; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -71,16 +69,14 @@ public class TestVarArgs extends CallGeneratorHelper { @Test(dataProvider = "variadicFunctions") public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff List paramTypes, List fields) throws Throwable { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { List args = makeArgs(arena, paramTypes, fields); MethodHandle checker = MethodHandles.insertArguments(MH_CHECK, 2, args); - MemorySegment writeBack = LINKER.upcallStub(checker, FunctionDescriptor.ofVoid(C_INT, C_POINTER), arena.scope()); + MemorySegment writeBack = LINKER.upcallStub(checker, FunctionDescriptor.ofVoid(C_INT, C_POINTER), arena); MemorySegment callInfo = arena.allocate(CallInfo.LAYOUT); MemoryLayout layout = MemoryLayout.sequenceLayout(args.size(), C_INT); MemorySegment argIDs = arena.allocate(layout); - MemorySegment callInfoPtr = callInfo; - CallInfo.writeback(callInfo, writeBack); CallInfo.argIDs(callInfo, argIDs); @@ -99,7 +95,7 @@ public class TestVarArgs extends CallGeneratorHelper { MethodHandle downcallHandle = LINKER.downcallHandle(VARARGS_ADDR, desc, varargIndex); List argValues = new ArrayList<>(); - argValues.add(callInfoPtr); // call info + argValues.add(callInfo); // call info argValues.add(args.size()); // size args.forEach(a -> argValues.add(a.value())); @@ -178,8 +174,9 @@ public class TestVarArgs extends CallGeneratorHelper { Arg varArg = args.get(index); MemoryLayout layout = varArg.layout; MethodHandle getter = varArg.getter; - try (Arena arena = Arena.openConfined()) { - MemorySegment seg = MemorySegment.ofAddress(ptr.address(), layout.byteSize(), arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment seg = ptr.asSlice(0, layout) + .reinterpret(arena, null); Object obj = getter.invoke(seg); varArg.check(obj); } catch (Throwable e) { diff --git a/test/jdk/java/foreign/TestVarHandleCombinators.java b/test/jdk/java/foreign/TestVarHandleCombinators.java index 28b05d02d62..cf748865d7e 100644 --- a/test/jdk/java/foreign/TestVarHandleCombinators.java +++ b/test/jdk/java/foreign/TestVarHandleCombinators.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -29,7 +29,6 @@ */ import java.lang.foreign.Arena; -import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import org.testng.annotations.Test; @@ -66,7 +65,8 @@ public class TestVarHandleCombinators { public void testAlign() { VarHandle vh = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_BYTE.withBitAlignment(16)); - MemorySegment segment = MemorySegment.allocateNative(1L, 2, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(1L, 2); vh.set(segment, 0L, (byte) 10); // fine, memory region is aligned assertEquals((byte) vh.get(segment, 0L), (byte) 10); } @@ -102,8 +102,8 @@ public class TestVarHandleCombinators { VarHandle vh = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT.withBitAlignment(32)); int count = 0; - try (Arena arena = Arena.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8, 4, arena.scope());; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(inner_size * outer_size * 8, 4); for (long i = 0; i < outer_size; i++) { for (long j = 0; j < inner_size; j++) { vh.set(segment, i * 40 + j * 8, count); diff --git a/test/jdk/java/foreign/ThrowingUpcall.java b/test/jdk/java/foreign/ThrowingUpcall.java deleted file mode 100644 index 70d2aba66ec..00000000000 --- a/test/jdk/java/foreign/ThrowingUpcall.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2021, 2022, 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. - * - * 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. - */ - -import java.lang.foreign.Arena; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -public class ThrowingUpcall extends NativeTestHelper { - - private static final MethodHandle downcallVoid; - private static final MethodHandle downcallNonVoid; - public static final MethodHandle MH_throwException; - - static { - System.loadLibrary("TestUpcall"); - downcallVoid = Linker.nativeLinker().downcallHandle( - findNativeOrThrow("f0_V__"), - FunctionDescriptor.ofVoid(C_POINTER) - ); - downcallNonVoid = Linker.nativeLinker().downcallHandle( - findNativeOrThrow("f10_I_I_"), - FunctionDescriptor.of(C_INT, C_INT, C_POINTER) - ); - - try { - MH_throwException = MethodHandles.lookup().findStatic(ThrowingUpcall.class, "throwException", - MethodType.methodType(void.class)); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } - - public static void throwException() throws Throwable { - throw new Throwable("Testing upcall exceptions"); - } - - public static void main(String[] args) throws Throwable { - if (args[0].equals("void")) { - testVoid(); - } else { - testNonVoid(); - } - } - - public static void testVoid() throws Throwable { - MethodHandle handle = MH_throwException; - MethodHandle invoker = MethodHandles.exactInvoker(MethodType.methodType(void.class)); - handle = MethodHandles.insertArguments(invoker, 0, handle); - - try (Arena arena = Arena.openConfined()) { - MemorySegment stub = Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), arena.scope()); - - downcallVoid.invoke(stub); // should call Shutdown.exit(1); - } - } - - public static void testNonVoid() throws Throwable { - MethodHandle handle = MethodHandles.identity(int.class); - handle = MethodHandles.collectArguments(handle, 0, MH_throwException); - MethodHandle invoker = MethodHandles.exactInvoker(MethodType.methodType(int.class, int.class)); - handle = MethodHandles.insertArguments(invoker, 0, handle); - - try (Arena arena = Arena.openConfined()) { - MemorySegment stub = Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.of(C_INT, C_INT), arena.scope()); - - downcallNonVoid.invoke(42, stub); // should call Shutdown.exit(1); - } - } - -} diff --git a/test/jdk/java/foreign/UpcallTestHelper.java b/test/jdk/java/foreign/UpcallTestHelper.java index fa4ff862d51..3860080cf21 100644 --- a/test/jdk/java/foreign/UpcallTestHelper.java +++ b/test/jdk/java/foreign/UpcallTestHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -37,13 +37,19 @@ import static org.testng.Assert.assertTrue; public class UpcallTestHelper extends NativeTestHelper { public record Output(List stdout, List stderr) { - private static void assertContains(List lines, String shouldInclude) { + private static void assertContains(List lines, String shouldInclude, String name) { assertTrue(lines.stream().anyMatch(line -> line.contains(shouldInclude)), - "Did not find '" + shouldInclude + "' in stderr"); + "Did not find '" + shouldInclude + "' in " + name); } - public void assertStdErrContains(String shouldInclude) { - assertContains(stderr, shouldInclude); + public Output assertStdErrContains(String shouldInclude) { + assertContains(stderr, shouldInclude, "stderr"); + return this; + } + + public Output assertStdOutContains(String shouldInclude) { + assertContains(stdout, shouldInclude, "stdout"); + return this; } } @@ -59,7 +65,7 @@ public class UpcallTestHelper extends NativeTestHelper { "--enable-preview", "--enable-native-access=ALL-UNNAMED", "-Djava.library.path=" + System.getProperty("java.library.path"), - "-Djdk.internal.foreign.ProgrammableUpcallHandler.USE_SPEC=" + useSpec, + "-Djdk.internal.foreign.UpcallLinker.USE_SPEC=" + useSpec, "-cp", Utils.TEST_CLASS_PATH, target.getName() )); diff --git a/test/jdk/java/foreign/arraystructs/TestArrayStructs.java b/test/jdk/java/foreign/arraystructs/TestArrayStructs.java index c331973f34c..7a4761c87c7 100644 --- a/test/jdk/java/foreign/arraystructs/TestArrayStructs.java +++ b/test/jdk/java/foreign/arraystructs/TestArrayStructs.java @@ -77,7 +77,7 @@ public class TestArrayStructs extends NativeTestHelper { FunctionDescriptor downcallDesc = baseDesc.insertArgumentLayouts(0, C_POINTER); // CB MemoryLayout[] elementLayouts = Collections.nCopies(numElements, C_CHAR).toArray(MemoryLayout[]::new); FunctionDescriptor upcallDesc = baseDesc.appendArgumentLayouts(elementLayouts); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { TestValue[] testArgs = genTestArgs(baseDesc, arena); MethodHandle downcallHandle = downcallHandle(functionName, downcallDesc); diff --git a/test/jdk/java/foreign/TestLayoutEquality.java b/test/jdk/java/foreign/callarranger/TestLayoutEquality.java similarity index 81% rename from test/jdk/java/foreign/TestLayoutEquality.java rename to test/jdk/java/foreign/callarranger/TestLayoutEquality.java index 84230d79d67..68127010c89 100644 --- a/test/jdk/java/foreign/TestLayoutEquality.java +++ b/test/jdk/java/foreign/callarranger/TestLayoutEquality.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,13 +25,17 @@ /* * @test * @enablePreview - * @modules java.base/jdk.internal.foreign + * @compile platform/PlatformLayouts.java + * @modules java.base/jdk.internal.foreign.abi + * @modules java.base/jdk.internal.foreign.layout * @run testng TestLayoutEquality */ -import java.lang.foreign.MemoryLayout; +import java.lang.foreign.AddressLayout; import java.lang.foreign.ValueLayout; -import jdk.internal.foreign.PlatformLayouts; + +import jdk.internal.foreign.layout.ValueLayouts; +import platform.PlatformLayouts; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -45,10 +49,10 @@ public class TestLayoutEquality { @Test(dataProvider = "layoutConstants") public void testReconstructedEquality(ValueLayout layout) { - ValueLayout newLayout = MemoryLayout.valueLayout(layout.carrier(), layout.order()); + ValueLayout newLayout = ValueLayouts.valueLayout(layout.carrier(), layout.order()); newLayout = newLayout.withBitAlignment(layout.bitAlignment()); - if (layout instanceof ValueLayout.OfAddress addressLayout && addressLayout.isUnbounded()) { - newLayout = ((ValueLayout.OfAddress)newLayout).asUnbounded(); + if (layout instanceof AddressLayout addressLayout && addressLayout.targetLayout().isPresent()) { + newLayout = ((AddressLayout)newLayout).withTargetLayout(addressLayout.targetLayout().get()); } // properties should be equal diff --git a/test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java index 26eb5e292c9..8a81e0f416b 100644 --- a/test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java @@ -26,6 +26,7 @@ * @test * @enablePreview * @requires sun.arch.data.model == "64" + * @compile platform/PlatformLayouts.java * @modules java.base/jdk.internal.foreign * java.base/jdk.internal.foreign.abi * java.base/jdk.internal.foreign.abi.aarch64 @@ -50,10 +51,10 @@ import java.lang.invoke.MethodType; import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.foreign.ValueLayout.ADDRESS; -import static jdk.internal.foreign.PlatformLayouts.AArch64.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*; +import static platform.PlatformLayouts.AArch64.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; diff --git a/test/jdk/java/foreign/callarranger/TestMacOsAArch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestMacOsAArch64CallArranger.java index a0acf4088a1..278808593c7 100644 --- a/test/jdk/java/foreign/callarranger/TestMacOsAArch64CallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestMacOsAArch64CallArranger.java @@ -26,6 +26,7 @@ * @test * @enablePreview * @requires sun.arch.data.model == "64" + * @compile platform/PlatformLayouts.java * @modules java.base/jdk.internal.foreign * java.base/jdk.internal.foreign.abi * java.base/jdk.internal.foreign.abi.aarch64 @@ -50,10 +51,10 @@ import java.lang.invoke.MethodType; import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.foreign.ValueLayout.ADDRESS; -import static jdk.internal.foreign.PlatformLayouts.AArch64.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*; +import static platform.PlatformLayouts.AArch64.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; diff --git a/test/jdk/java/foreign/callarranger/TestRISCV64CallArranger.java b/test/jdk/java/foreign/callarranger/TestRISCV64CallArranger.java index f58abb5eef4..40c5d8a12bc 100644 --- a/test/jdk/java/foreign/callarranger/TestRISCV64CallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestRISCV64CallArranger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Institute of Software, Chinese Academy of Sciences. * All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -28,6 +28,7 @@ * @test * @enablePreview * @requires sun.arch.data.model == "64" + * @compile platform/PlatformLayouts.java * @modules java.base/jdk.internal.foreign * java.base/jdk.internal.foreign.abi * java.base/jdk.internal.foreign.abi.riscv64 @@ -39,7 +40,6 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.StructLayout; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; import jdk.internal.foreign.abi.LinkerOptions; @@ -49,14 +49,15 @@ import jdk.internal.foreign.abi.VMStorage; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodType; import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.foreign.ValueLayout.ADDRESS; -import static jdk.internal.foreign.PlatformLayouts.RISCV64.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.*; import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.Regs.*; +import static platform.PlatformLayouts.RISCV64.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -212,27 +213,6 @@ public class TestRISCV64CallArranger extends CallArrangerTestBase { // s.b bufferLoad(8, double.class), vmStore(f11, double.class), } - }, - // struct __attribute__((__packed__)) s { float a; double b; }; - { MemoryLayout.structLayout(C_FLOAT, C_DOUBLE), - new Binding[]{ - dup(), - // s.a - bufferLoad(0, float.class), vmStore(f10, float.class), - // s.b - bufferLoad(4, double.class), vmStore(f11, double.class), - } - }, - // struct s { float a; float b __attribute__ ((aligned (8))); } - { MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(32), - C_FLOAT, MemoryLayout.paddingLayout(32)), - new Binding[]{ - dup(), - // s.a - bufferLoad(0, float.class), vmStore(f10, float.class), - // s.b - bufferLoad(8, float.class), vmStore(f11, float.class), - } } }; } @@ -277,7 +257,7 @@ public class TestRISCV64CallArranger extends CallArrangerTestBase { @Test public void testStructFA2() { - MemoryLayout fa = MemoryLayout.structLayout(C_FLOAT, C_DOUBLE); + MemoryLayout fa = MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(32), C_DOUBLE); MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.of(fa, C_FLOAT, C_INT, fa); @@ -297,7 +277,7 @@ public class TestRISCV64CallArranger extends CallArrangerTestBase { dup(), bufferLoad(0, float.class), vmStore(f11, float.class), - bufferLoad(4, double.class), + bufferLoad(8, double.class), vmStore(f12, double.class) } }); @@ -309,7 +289,7 @@ public class TestRISCV64CallArranger extends CallArrangerTestBase { bufferStore(0, float.class), dup(), vmLoad(f11, double.class), - bufferStore(4, double.class) + bufferStore(8, double.class) }); } diff --git a/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java b/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java index a17d3b79af7..4fa579b47d3 100644 --- a/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,6 +25,7 @@ /* * @test * @enablePreview + * @compile platform/PlatformLayouts.java * @modules java.base/jdk.internal.foreign * java.base/jdk.internal.foreign.abi * java.base/jdk.internal.foreign.abi.x64 @@ -47,10 +48,10 @@ import org.testng.annotations.Test; import java.lang.invoke.MethodType; import static java.lang.foreign.ValueLayout.ADDRESS; -import static jdk.internal.foreign.PlatformLayouts.SysV.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.x64.X86_64Architecture.*; import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*; +import static platform.PlatformLayouts.SysV.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -144,66 +145,6 @@ public class TestSysVCallArranger extends CallArrangerTestBase { assertEquals(bindings.nVectorArgs(), 0); } - @Test - public void testNestedStructsUnaligned() { - MemoryLayout POINT = MemoryLayout.structLayout( - C_INT, - MemoryLayout.structLayout( - C_LONG, - C_INT - ) - ); - MethodType mt = MethodType.methodType(void.class, MemorySegment.class); - FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - - assertFalse(bindings.isInMemoryReturn()); - CallingSequence callingSequence = bindings.callingSequence(); - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); - assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); - - checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, - { dup(), bufferLoad(0, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class), - bufferLoad(8, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 8), long.class)}, - { vmStore(rax, long.class) }, - }); - - checkReturnBindings(callingSequence, new Binding[]{}); - - assertEquals(bindings.nVectorArgs(), 0); - } - - @Test - public void testNestedUnionUnaligned() { - MemoryLayout POINT = MemoryLayout.structLayout( - C_INT, - MemoryLayout.unionLayout( - MemoryLayout.structLayout(C_INT, C_INT), - C_LONG - ) - ); - MethodType mt = MethodType.methodType(void.class, MemorySegment.class); - FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - - assertFalse(bindings.isInMemoryReturn()); - CallingSequence callingSequence = bindings.callingSequence(); - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); - assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); - - checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, - { dup(), bufferLoad(0, long.class), vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class), - bufferLoad(8, int.class), vmStore(stackStorage(STACK_SLOT_SIZE, 8), int.class)}, - { vmStore(rax, long.class) }, - }); - - checkReturnBindings(callingSequence, new Binding[]{}); - - assertEquals(bindings.nVectorArgs(), 0); - } - @Test public void testIntegerRegs() { MethodType mt = MethodType.methodType(void.class, diff --git a/test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java index 9fe3566001c..2e0b42cd48e 100644 --- a/test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,6 +25,7 @@ /* * @test * @enablePreview + * @compile platform/PlatformLayouts.java * @modules java.base/jdk.internal.foreign * java.base/jdk.internal.foreign.abi * java.base/jdk.internal.foreign.abi.aarch64 @@ -34,7 +35,6 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.StructLayout; import java.lang.foreign.MemorySegment; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; @@ -49,10 +49,10 @@ import java.lang.invoke.MethodType; import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.foreign.ValueLayout.ADDRESS; -import static jdk.internal.foreign.PlatformLayouts.AArch64.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*; +import static platform.PlatformLayouts.AArch64.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; diff --git a/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java b/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java index 968f3e8db29..8257eaa7eae 100644 --- a/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,6 +25,8 @@ /* * @test * @enablePreview + * @requires sun.arch.data.model == "64" + * @compile platform/PlatformLayouts.java * @modules java.base/jdk.internal.foreign * java.base/jdk.internal.foreign.abi * java.base/jdk.internal.foreign.abi.x64 @@ -48,11 +50,11 @@ import java.lang.invoke.MethodType; import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.foreign.ValueLayout.ADDRESS; -import static jdk.internal.foreign.PlatformLayouts.Win64.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.Binding.copy; import static jdk.internal.foreign.abi.x64.X86_64Architecture.*; import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*; +import static platform.PlatformLayouts.Win64.*; import static org.testng.Assert.*; diff --git a/src/java.base/share/classes/jdk/internal/foreign/PlatformLayouts.java b/test/jdk/java/foreign/callarranger/platform/PlatformLayouts.java similarity index 71% rename from src/java.base/share/classes/jdk/internal/foreign/PlatformLayouts.java rename to test/jdk/java/foreign/callarranger/platform/PlatformLayouts.java index 11d16294316..45f7218c0ad 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/PlatformLayouts.java +++ b/test/jdk/java/foreign/callarranger/platform/PlatformLayouts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -23,23 +23,25 @@ * questions. * */ -package jdk.internal.foreign; +package platform; +import jdk.internal.foreign.abi.SharedUtils; + +import java.lang.foreign.AddressLayout; import java.lang.foreign.ValueLayout; public final class PlatformLayouts { - private PlatformLayouts() { - //just the one - } + // Suppresses default constructor, ensuring non-instantiability. + private PlatformLayouts() {} /** * This class defines layout constants modelling standard primitive types supported by the x64 SystemV ABI. */ public static final class SysV { - private SysV() { - //just the one - } + + // Suppresses default constructor, ensuring non-instantiability. + private SysV() {} /** * The {@code bool} native type. @@ -54,42 +56,38 @@ public final class PlatformLayouts { /** * The {@code short} native type. */ - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; /** * The {@code int} native type. */ - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; /** * The {@code long} native type. */ - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; /** * The {@code long long} native type. */ - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; /** * The {@code float} native type. */ - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; /** * The {@code double} native type. */ - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); + public static final AddressLayout C_POINTER = SharedUtils.C_POINTER;; - /** - * The {@code va_list} native type, as it is passed to a function. - */ - public static final ValueLayout.OfAddress C_VA_LIST = SysV.C_POINTER; } /** @@ -97,9 +95,8 @@ public final class PlatformLayouts { */ public static final class Win64 { - private Win64() { - //just the one - } + // Suppresses default constructor, ensuring non-instantiability. + private Win64() {} /** * The {@code bool} native type. @@ -114,41 +111,37 @@ public final class PlatformLayouts { /** * The {@code short} native type. */ - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; /** * The {@code int} native type. */ - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; /** * The {@code long} native type. */ - public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT.withBitAlignment(32); + public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT; /** * The {@code long long} native type. */ - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; /** * The {@code float} native type. */ - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; /** * The {@code double} native type. */ - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); + public static final AddressLayout C_POINTER = SharedUtils.C_POINTER; - /** - * The {@code va_list} native type, as it is passed to a function. - */ - public static final ValueLayout.OfAddress C_VA_LIST = Win64.C_POINTER; } /** @@ -156,9 +149,8 @@ public final class PlatformLayouts { */ public static final class AArch64 { - private AArch64() { - //just the one - } + // Suppresses default constructor, ensuring non-instantiability. + private AArch64() {} /** * The {@code bool} native type. @@ -173,48 +165,44 @@ public final class PlatformLayouts { /** * The {@code short} native type. */ - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; /** * The {@code int} native type. */ - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; /** * The {@code long} native type. */ - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; /** * The {@code long long} native type. */ - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; /** * The {@code float} native type. */ - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; /** * The {@code double} native type. */ - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); + public static final AddressLayout C_POINTER = SharedUtils.C_POINTER; - /** - * The {@code va_list} native type, as it is passed to a function. - */ - public static final ValueLayout.OfAddress C_VA_LIST = AArch64.C_POINTER; } public static final class RISCV64 { - private RISCV64() { - //just the one - } + + // Suppresses default constructor, ensuring non-instantiability. + private RISCV64() {} /** * The {@code bool} native type. @@ -229,41 +217,38 @@ public final class PlatformLayouts { /** * The {@code short} native type. */ - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; /** * The {@code int} native type. */ - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; /** * The {@code long} native type. */ - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; /** * The {@code long long} native type. */ - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; /** * The {@code float} native type. */ - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; /** * The {@code double} native type. */ - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); + public static final AddressLayout C_POINTER = SharedUtils.C_POINTER; - /** - * The {@code va_list} native type, as it is passed to a function. - */ - public static final ValueLayout.OfAddress C_VA_LIST = RISCV64.C_POINTER; } + } diff --git a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java index ebfb36953c5..7d02b71d5b6 100644 --- a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java +++ b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -66,13 +66,14 @@ public class TestCaptureCallState extends NativeTestHelper { @Test(dataProvider = "cases") public void testSavedThreadLocal(SaveValuesCase testCase) throws Throwable { - Linker.Option.CaptureCallState stl = Linker.Option.captureCallState(testCase.threadLocalName()); + Linker.Option stl = Linker.Option.captureCallState(testCase.threadLocalName()); MethodHandle handle = downcallHandle(testCase.nativeTarget(), testCase.nativeDesc(), stl); - VarHandle errnoHandle = stl.layout().varHandle(groupElement(testCase.threadLocalName())); + StructLayout capturedStateLayout = Linker.Option.captureStateLayout(); + VarHandle errnoHandle = capturedStateLayout.varHandle(groupElement(testCase.threadLocalName())); - try (Arena arena = Arena.openConfined()) { - MemorySegment saveSeg = arena.allocate(stl.layout()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment saveSeg = arena.allocate(capturedStateLayout); int testValue = 42; boolean needsAllocator = testCase.nativeDesc().returnLayout().map(StructLayout.class::isInstance).orElse(false); Object result = needsAllocator diff --git a/test/jdk/java/foreign/channels/AbstractChannelsTest.java b/test/jdk/java/foreign/channels/AbstractChannelsTest.java index d3c96bba986..6c9e64a40a2 100644 --- a/test/jdk/java/foreign/channels/AbstractChannelsTest.java +++ b/test/jdk/java/foreign/channels/AbstractChannelsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -24,7 +24,6 @@ import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Random; @@ -68,15 +67,15 @@ public class AbstractChannelsTest { static final Random RANDOM = RandomFactory.getRandom(); - static ByteBuffer segmentBufferOfSize(SegmentScope session, int size) { - var segment = MemorySegment.allocateNative(size, 1, session); + static ByteBuffer segmentBufferOfSize(Arena session, int size) { + var segment = session.allocate(size, 1); for (int i = 0; i < size; i++) { segment.set(JAVA_BYTE, i, ((byte)RANDOM.nextInt())); } return segment.asByteBuffer(); } - static ByteBuffer[] segmentBuffersOfSize(int len, SegmentScope session, int size) { + static ByteBuffer[] segmentBuffersOfSize(int len, Arena session, int size) { ByteBuffer[] bufs = new ByteBuffer[len]; for (int i = 0; i < len; i++) bufs[i] = segmentBufferOfSize(session, size); @@ -88,7 +87,7 @@ public class AbstractChannelsTest { * where heap can be from the global session or session-less, and direct are * associated with the given session. */ - static ByteBuffer[] mixedBuffersOfSize(int len, SegmentScope session, int size) { + static ByteBuffer[] mixedBuffersOfSize(int len, Arena session, int size) { ByteBuffer[] bufs; boolean atLeastOneSessionBuffer = false; do { @@ -153,9 +152,9 @@ public class AbstractChannelsTest { static class ArenaSupplier implements Supplier { static final Supplier NEW_CONFINED = - new ArenaSupplier(Arena::openConfined, "confined arena"); + new ArenaSupplier(Arena::ofConfined, "confined arena"); static final Supplier NEW_SHARED = - new ArenaSupplier(Arena::openShared, "shared arena"); + new ArenaSupplier(Arena::ofShared, "shared arena"); private final Supplier supplier; private final String str; diff --git a/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java b/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java index 3091514cc58..f85dfe81596 100644 --- a/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java +++ b/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -75,7 +75,8 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { var server = AsynchronousServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel); var drop = arenaSupplier.get()) { - var segment = MemorySegment.allocateNative(10, 1, drop.scope()); + Arena scope = drop; + var segment = scope.allocate(10, 1); var bb = segment.asByteBuffer(); var bba = new ByteBuffer[] { bb }; List> ioOps = List.of( @@ -108,8 +109,8 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { var server = AsynchronousServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel)) { Arena drop = arenaSupplier.get(); - ByteBuffer bb = segmentBufferOfSize(drop.scope(), 64); - ByteBuffer[] buffers = segmentBuffersOfSize(8, drop.scope(), 32); + ByteBuffer bb = segmentBufferOfSize(drop, 64); + ByteBuffer[] buffers = segmentBuffersOfSize(8, drop, 32); drop.close(); { assertCauses(expectThrows(EE, () -> connectedChannel.read(bb).get()), IOE, ISE); @@ -160,8 +161,10 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { var assc = AsynchronousServerSocketChannel.open(); var asc2 = connectChannels(assc, asc1); var scp = drop = arenaSupplier.get()) { - MemorySegment segment1 = MemorySegment.allocateNative(10, 1, drop.scope()); - MemorySegment segment2 = MemorySegment.allocateNative(10, 1, drop.scope()); + Arena scope1 = drop; + MemorySegment segment1 = scope1.allocate(10, 1); + Arena scope = drop; + MemorySegment segment2 = scope.allocate(10, 1); for (int i = 0; i < 10; i++) { segment1.set(JAVA_BYTE, i, (byte) i); } @@ -184,8 +187,8 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { assertEquals(bb2.flip(), ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } { // Gathering/Scattering variants - var writeBuffers = mixedBuffersOfSize(16, drop.scope(), 32); - var readBuffers = mixedBuffersOfSize(16, drop.scope(), 32); + var writeBuffers = mixedBuffersOfSize(16, drop, 32); + var readBuffers = mixedBuffersOfSize(16, drop, 32); long expectedCount = remaining(writeBuffers); var writeHandler = new TestHandler(); asc1.write(writeBuffers, 0, 16, 30L, SECONDS, null, writeHandler); @@ -207,7 +210,8 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { var assc = AsynchronousServerSocketChannel.open(); var asc2 = connectChannels(assc, asc1); var drop = arenaSupplier.get()) { - var segment = MemorySegment.allocateNative(10, 1, drop.scope()); + Arena scope = drop; + var segment = scope.allocate(10, 1); var bb = segment.asByteBuffer(); var bba = new ByteBuffer[] { bb }; List> readOps = List.of( @@ -252,7 +256,7 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { // write until socket buffer is full so as to create the conditions // for when a write does not complete immediately - var bba = segmentBuffersOfSize(32, drop.scope(), 128); + var bba = segmentBuffersOfSize(32, drop, 128); TestHandler handler; outstandingWriteOps.getAndIncrement(); asc1.write(bba, 0, bba.length, timeout, SECONDS, null, @@ -261,7 +265,7 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { super.completed(result, att); bytesWritten.addAndGet(result); if (continueWriting.get()) { - var bba = segmentBuffersOfSize(32, drop.scope(), 128); + var bba = segmentBuffersOfSize(32, drop, 128); outstandingWriteOps.getAndIncrement(); asc1.write(bba, 0, bba.length, timeout, SECONDS, null, this); } diff --git a/test/jdk/java/foreign/channels/TestSocketChannels.java b/test/jdk/java/foreign/channels/TestSocketChannels.java index 98cc84e0bf7..445e0a9a236 100644 --- a/test/jdk/java/foreign/channels/TestSocketChannels.java +++ b/test/jdk/java/foreign/channels/TestSocketChannels.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -65,7 +65,7 @@ public class TestSocketChannels extends AbstractChannelsTest { var server = ServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel)) { Arena drop = arenaSupplier.get(); - ByteBuffer bb = segmentBufferOfSize(drop.scope(), 16); + ByteBuffer bb = segmentBufferOfSize(drop, 16); drop.close(); assertMessage(expectThrows(ISE, () -> channel.read(bb)), "Already closed"); assertMessage(expectThrows(ISE, () -> channel.read(new ByteBuffer[] {bb})), "Already closed"); @@ -84,7 +84,7 @@ public class TestSocketChannels extends AbstractChannelsTest { var server = ServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel)) { Arena drop = arenaSupplier.get(); - ByteBuffer[] buffers = segmentBuffersOfSize(8, drop.scope(), 16); + ByteBuffer[] buffers = segmentBuffersOfSize(8, drop, 16); drop.close(); assertMessage(expectThrows(ISE, () -> channel.write(buffers)), "Already closed"); assertMessage(expectThrows(ISE, () -> channel.read(buffers)), "Already closed"); @@ -102,8 +102,10 @@ public class TestSocketChannels extends AbstractChannelsTest { var ssc = ServerSocketChannel.open(); var sc2 = connectChannels(ssc, sc1); var scp = drop = arenaSupplier.get()) { - MemorySegment segment1 = MemorySegment.allocateNative(10, 1, drop.scope()); - MemorySegment segment2 = MemorySegment.allocateNative(10, 1, drop.scope()); + Arena scope1 = drop; + MemorySegment segment1 = scope1.allocate(10, 1); + Arena scope = drop; + MemorySegment segment2 = scope.allocate(10, 1); for (int i = 0; i < 10; i++) { segment1.set(JAVA_BYTE, i, (byte) i); } @@ -141,7 +143,8 @@ public class TestSocketChannels extends AbstractChannelsTest { var server = ServerSocketChannel.open(); var connected = connectChannels(server, channel); var drop = arenaSupplier.get()) { - var segment = MemorySegment.allocateNative(10, 1, drop.scope()); + Arena scope = drop; + var segment = scope.allocate(10, 1); ByteBuffer bb = segment.asByteBuffer(); List ioOps = List.of( () -> channel.write(bb), @@ -171,8 +174,8 @@ public class TestSocketChannels extends AbstractChannelsTest { var ssc = ServerSocketChannel.open(); var sc2 = connectChannels(ssc, sc1); var scp = drop = arenaSupplier.get()) { - var writeBuffers = mixedBuffersOfSize(32, drop.scope(), 64); - var readBuffers = mixedBuffersOfSize(32, drop.scope(), 64); + var writeBuffers = mixedBuffersOfSize(32, drop, 64); + var readBuffers = mixedBuffersOfSize(32, drop, 64); long expectedCount = remaining(writeBuffers); assertEquals(writeNBytes(sc1, writeBuffers, 0, 32, expectedCount), expectedCount); assertEquals(readNBytes(sc2, readBuffers, 0, 32, expectedCount), expectedCount); @@ -189,10 +192,10 @@ public class TestSocketChannels extends AbstractChannelsTest { var sc2 = connectChannels(ssc, sc1); var drop1 = arenaSupplier.get(); var drop2 = arenaSupplier.get()) { - var writeBuffers = Stream.of(mixedBuffersOfSize(16, drop1.scope(), 64), mixedBuffersOfSize(16, drop2.scope(), 64)) + var writeBuffers = Stream.of(mixedBuffersOfSize(16, drop1, 64), mixedBuffersOfSize(16, drop2, 64)) .flatMap(Arrays::stream) .toArray(ByteBuffer[]::new); - var readBuffers = Stream.of(mixedBuffersOfSize(16, drop1.scope(), 64), mixedBuffersOfSize(16, drop2.scope(), 64)) + var readBuffers = Stream.of(mixedBuffersOfSize(16, drop1, 64), mixedBuffersOfSize(16, drop2, 64)) .flatMap(Arrays::stream) .toArray(ByteBuffer[]::new); diff --git a/test/jdk/java/foreign/dontrelease/TestDontRelease.java b/test/jdk/java/foreign/dontrelease/TestDontRelease.java index 5c34a379866..4a258352b36 100644 --- a/test/jdk/java/foreign/dontrelease/TestDontRelease.java +++ b/test/jdk/java/foreign/dontrelease/TestDontRelease.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -25,10 +25,12 @@ * @test * @enablePreview * @library ../ /test/lib + * @modules java.base/jdk.internal.ref java.base/jdk.internal.foreign * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64" * @run testng/othervm --enable-native-access=ALL-UNNAMED TestDontRelease */ +import jdk.internal.foreign.MemorySessionImpl; import org.testng.annotations.Test; import java.lang.foreign.Arena; @@ -49,9 +51,9 @@ public class TestDontRelease extends NativeTestHelper { @Test public void testDontRelease() { MethodHandle handle = downcallHandle("test_ptr", FunctionDescriptor.ofVoid(ADDRESS)); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(JAVA_INT); - arena.scope().whileAlive(() -> { + ((MemorySessionImpl)arena.scope()).whileAlive(() -> { Thread t = new Thread(() -> { try { // acquire of the segment should fail here, diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java index 3743ac73835..71a8a6bb58e 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -24,7 +24,7 @@ package org.openjdk.foreigntest; import java.lang.foreign.*; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; public class PanamaMainDirect { public static void main(String[] args) { @@ -40,7 +40,7 @@ public class PanamaMainDirect { public static void testDirectAccessMemorySegment() { System.out.println("Trying to get MemorySegment"); - MemorySegment.ofAddress(0, 4000, SegmentScope.global()); + MemorySegment.NULL.reinterpret(10); System.out.println("Got MemorySegment"); } } diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java index 0d4d7bb4bb0..ef75fa822c0 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -24,7 +24,7 @@ package org.openjdk.foreigntest; import java.lang.foreign.*; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.invoke.*; public class PanamaMainInvoke { @@ -43,9 +43,9 @@ public class PanamaMainInvoke { public static void testInvokeMemorySegment() throws Throwable { System.out.println("Trying to get MemorySegment"); - var mh = MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", - MethodType.methodType(MemorySegment.class, long.class, long.class, SegmentScope.class)); - var seg = (MemorySegment)mh.invokeExact(0L, 4000L, SegmentScope.global()); + var mh = MethodHandles.lookup().findVirtual(MemorySegment.class, "reinterpret", + MethodType.methodType(MemorySegment.class, long.class)); + var seg = (MemorySegment)mh.invokeExact(MemorySegment.NULL, 10L); System.out.println("Got MemorySegment"); } } diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java index 68aebc73ab5..a32f3edca06 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -24,7 +24,7 @@ package org.openjdk.foreigntest; import java.lang.foreign.*; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.reflect.Method; public class PanamaMainReflection { @@ -42,8 +42,8 @@ public class PanamaMainReflection { public static void testReflectionMemorySegment() throws Throwable { System.out.println("Trying to get MemorySegment"); - Method method = MemorySegment.class.getDeclaredMethod("ofAddress", long.class, long.class, SegmentScope.class); - method.invoke(null, 0L, 4000L, SegmentScope.global()); + Method method = MemorySegment.class.getDeclaredMethod("reinterpret", long.class); + method.invoke(MemorySegment.NULL, 10L); System.out.println("Got MemorySegment"); } } diff --git a/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java b/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java index 090a57bcc7b..dc212d51d09 100644 --- a/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java +++ b/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -23,7 +23,8 @@ package handle.invoker; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.AddressLayout; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; @@ -39,6 +40,7 @@ import java.nio.charset.Charset; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.function.Consumer; public class MethodHandleInvoker { public void call(MethodHandle methodHandle) throws Throwable { @@ -72,7 +74,8 @@ public class MethodHandleInvoker { addDefaultMapping(MemorySegment.class, MemorySegment.NULL); addDefaultMapping(MemoryLayout.class, ValueLayout.JAVA_INT); addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid()); - addDefaultMapping(SegmentScope.class, SegmentScope.auto()); + addDefaultMapping(Arena.class, Arena.ofAuto()); + addDefaultMapping(MemorySegment.Scope.class, Arena.ofAuto().scope()); addDefaultMapping(SegmentAllocator.class, SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[10]))); addDefaultMapping(ValueLayout.OfByte.class, ValueLayout.JAVA_BYTE); addDefaultMapping(ValueLayout.OfBoolean.class, ValueLayout.JAVA_BOOLEAN); @@ -82,8 +85,9 @@ public class MethodHandleInvoker { addDefaultMapping(ValueLayout.OfFloat.class, ValueLayout.JAVA_FLOAT); addDefaultMapping(ValueLayout.OfLong.class, ValueLayout.JAVA_LONG); addDefaultMapping(ValueLayout.OfDouble.class, ValueLayout.JAVA_DOUBLE); - addDefaultMapping(ValueLayout.OfAddress.class, ValueLayout.ADDRESS); + addDefaultMapping(AddressLayout.class, ValueLayout.ADDRESS); addDefaultMapping(SymbolLookup.class, SymbolLookup.loaderLookup()); + addDefaultMapping(Consumer.class, (Consumer)(Object o) -> {}); addDefaultMapping(byte.class, (byte)0); addDefaultMapping(boolean.class, true); addDefaultMapping(char.class, (char)0); diff --git a/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java b/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java index 62014baf8af..4042e93e771 100644 --- a/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java +++ b/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -23,7 +23,7 @@ package handle.lookup; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.invoke.MethodHandle; @@ -34,6 +34,7 @@ import java.lang.foreign.MemorySegment; import java.lang.foreign.SymbolLookup; import java.nio.file.Path; +import java.util.function.Consumer; import org.testng.annotations.*; @@ -50,20 +51,20 @@ public class MethodHandleLookup { return new Object[][]{ { MethodHandles.lookup().findStatic(Linker.class, "nativeLinker", MethodType.methodType(Linker.class)), "Linker::nativeLinker" }, - { MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", - MethodType.methodType(MemorySegment.class, long.class, long.class)), - "MemorySegment::ofAddress/2" }, - { MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", - MethodType.methodType(MemorySegment.class, long.class, long.class, SegmentScope.class)), - "MemorySegment::ofAddress/3" }, - { MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", - MethodType.methodType(MemorySegment.class, long.class, long.class, SegmentScope.class, Runnable.class)), - "MemorySegment::ofAddress/4" }, + { MethodHandles.lookup().findVirtual(MemorySegment.class, "reinterpret", + MethodType.methodType(MemorySegment.class, long.class)), + "MemorySegment::reinterpret/1" }, + { MethodHandles.lookup().findVirtual(MemorySegment.class, "reinterpret", + MethodType.methodType(MemorySegment.class, Arena.class, Consumer.class)), + "MemorySegment::reinterpret/2" }, + { MethodHandles.lookup().findVirtual(MemorySegment.class, "reinterpret", + MethodType.methodType(MemorySegment.class, long.class, Arena.class, Consumer.class)), + "MemorySegment::reinterpret/3" }, { MethodHandles.lookup().findStatic(SymbolLookup.class, "libraryLookup", - MethodType.methodType(SymbolLookup.class, String.class, SegmentScope.class)), + MethodType.methodType(SymbolLookup.class, String.class, Arena.class)), "SymbolLookup::libraryLookup(String)" }, { MethodHandles.lookup().findStatic(SymbolLookup.class, "libraryLookup", - MethodType.methodType(SymbolLookup.class, Path.class, SegmentScope.class)), + MethodType.methodType(SymbolLookup.class, Path.class, Arena.class)), "SymbolLookup::libraryLookup(Path)" }, }; } catch (Throwable ex) { diff --git a/test/jdk/java/foreign/libAddressDereference.c b/test/jdk/java/foreign/libAddressDereference.c new file mode 100644 index 00000000000..b78b3d02f3e --- /dev/null +++ b/test/jdk/java/foreign/libAddressDereference.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, 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. + * + * 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. + * + */ + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +EXPORT void* get_addr(void* align) { + return align; +} + +EXPORT void get_addr_cb(void* align, void (*cb)(void*)) { + cb(align); +} diff --git a/test/jdk/java/foreign/nested/TestNested.java b/test/jdk/java/foreign/nested/TestNested.java index e64045be49e..9868349f990 100644 --- a/test/jdk/java/foreign/nested/TestNested.java +++ b/test/jdk/java/foreign/nested/TestNested.java @@ -55,7 +55,7 @@ public class TestNested extends NativeTestHelper { @Test(dataProvider = "nestedLayouts") public void testNested(GroupLayout layout) throws Throwable { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { Random random = new Random(0); TestValue testValue = genTestValue(random, layout, arena); diff --git a/test/jdk/java/foreign/normalize/TestNormalize.java b/test/jdk/java/foreign/normalize/TestNormalize.java index 048a7d5b5ea..aa8d829d35d 100644 --- a/test/jdk/java/foreign/normalize/TestNormalize.java +++ b/test/jdk/java/foreign/normalize/TestNormalize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -36,11 +36,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.lang.foreign.Arena; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.Linker; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -115,10 +111,10 @@ public class TestNormalize extends NativeTestHelper { MethodHandle downcallHandle = LINKER.downcallHandle(target, downcallDesc); downcallHandle = MethodHandles.filterReturnValue(downcallHandle, toInt); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { int[] box = new int[1]; saver = MethodHandles.insertArguments(saver, 1, box); - MemorySegment upcallStub = LINKER.upcallStub(saver, upcallDesc, arena.scope()); + MemorySegment upcallStub = LINKER.upcallStub(saver, upcallDesc, arena); int dirtyValue = testValue | hobMask; // set all bits that should not be set // test after JIT as well @@ -185,8 +181,8 @@ public class TestNormalize extends NativeTestHelper { boolean[] box = new boolean[1]; MethodHandle upcallTarget = MethodHandles.insertArguments(SAVE_BOOLEAN, 1, box); - try (Arena arena = Arena.openConfined()) { - MemorySegment callback = LINKER.upcallStub(upcallTarget, FunctionDescriptor.ofVoid(JAVA_BOOLEAN), arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment callback = LINKER.upcallStub(upcallTarget, FunctionDescriptor.ofVoid(JAVA_BOOLEAN), arena); boolean result = (boolean) target.invokeExact(callback, testValue); assertEquals(box[0], expected); assertEquals(result, expected); diff --git a/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java b/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java index 3a245f19cf9..0760be0a8a4 100644 --- a/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java +++ b/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -115,8 +115,8 @@ public class TestAsyncStackWalk extends NativeTestHelper { static boolean didStackWalk; public static void main(String[] args) throws Throwable { - try (Arena arena = Arena.openConfined()) { - MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), arena); invocations = 0; didStackWalk = false; payload(stub); diff --git a/test/jdk/java/foreign/stackwalk/TestReentrantUpcalls.java b/test/jdk/java/foreign/stackwalk/TestReentrantUpcalls.java index fce949c05e1..cd28661f45c 100644 --- a/test/jdk/java/foreign/stackwalk/TestReentrantUpcalls.java +++ b/test/jdk/java/foreign/stackwalk/TestReentrantUpcalls.java @@ -69,9 +69,9 @@ public class TestReentrantUpcalls extends NativeTestHelper { FunctionDescriptor descriptor = FunctionDescriptor.ofVoid(C_INT, C_POINTER); MethodHandle downcallHandle = downcallHandle("do_recurse", descriptor); - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MemorySegment stub = LINKER.upcallStub( - MethodHandles.insertArguments(MH_m, 2, downcallHandle), descriptor, arena.scope()); + MethodHandles.insertArguments(MH_m, 2, downcallHandle), descriptor, arena); downcallHandle.invokeExact(0, stub); } diff --git a/test/jdk/java/foreign/stackwalk/TestStackWalk.java b/test/jdk/java/foreign/stackwalk/TestStackWalk.java index ce4afc843cd..da3b11885b0 100644 --- a/test/jdk/java/foreign/stackwalk/TestStackWalk.java +++ b/test/jdk/java/foreign/stackwalk/TestStackWalk.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -114,8 +114,8 @@ public class TestStackWalk extends NativeTestHelper { static boolean armed; public static void main(String[] args) throws Throwable { - try (Arena arena = Arena.openConfined()) { - MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), arena); armed = false; for (int i = 0; i < 20_000; i++) { payload(stub); // warmup diff --git a/test/jdk/java/foreign/trivial/TestTrivial.java b/test/jdk/java/foreign/trivial/TestTrivial.java new file mode 100644 index 00000000000..98a89b2b8e7 --- /dev/null +++ b/test/jdk/java/foreign/trivial/TestTrivial.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023, 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. + * + * 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. + */ + +/* + * @test + * @enablePreview + * @library ../ /test/lib + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64" + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestTrivial + */ + +import org.testng.annotations.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.StructLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.VarHandle; + +import static org.testng.Assert.assertEquals; + +public class TestTrivial extends NativeTestHelper { + + static { + System.loadLibrary("Trivial"); + } + + @Test + public void testEmpty() throws Throwable { + MethodHandle handle = downcallHandle("empty", FunctionDescriptor.ofVoid(), Linker.Option.isTrivial()); + handle.invokeExact(); + } + + @Test + public void testIdentity() throws Throwable { + MethodHandle handle = downcallHandle("identity", FunctionDescriptor.of(C_INT, C_INT), Linker.Option.isTrivial()); + int result = (int) handle.invokeExact(42); + assertEquals(result, 42); + } + + @Test + public void testWithReturnBuffer() throws Throwable { + StructLayout bigLayout = MemoryLayout.structLayout( + C_LONG_LONG.withName("x"), + C_LONG_LONG.withName("y")); + + MethodHandle handle = downcallHandle("with_return_buffer", FunctionDescriptor.of(bigLayout), Linker.Option.isTrivial()); + VarHandle vhX = bigLayout.varHandle(MemoryLayout.PathElement.groupElement("x")); + VarHandle vhY = bigLayout.varHandle(MemoryLayout.PathElement.groupElement("y")); + try (Arena arena = Arena.ofConfined()) { + MemorySegment result = (MemorySegment) handle.invokeExact((SegmentAllocator) arena); + long x = (long) vhX.get(result); + assertEquals(x, 10); + long y = (long) vhY.get(result); + assertEquals(y, 11); + } + } + + @Test + public void testCaptureErrno() throws Throwable { + Linker.Option ccs = Linker.Option.captureCallState("errno"); + MethodHandle handle = downcallHandle("capture_errno", FunctionDescriptor.ofVoid(C_INT), Linker.Option.isTrivial(), ccs); + StructLayout capturedStateLayout = Linker.Option.captureStateLayout(); + VarHandle errnoHandle = capturedStateLayout.varHandle(MemoryLayout.PathElement.groupElement("errno")); + try (Arena arena = Arena.ofConfined()) { + MemorySegment captureSeg = arena.allocate(capturedStateLayout); + handle.invokeExact(captureSeg, 42); + int capturedErrno = (int) errnoHandle.get(captureSeg); + assertEquals(capturedErrno, 42); + } + } + + +} diff --git a/test/jdk/java/foreign/trivial/TestTrivialUpcall.java b/test/jdk/java/foreign/trivial/TestTrivialUpcall.java new file mode 100644 index 00000000000..bfba6f260d5 --- /dev/null +++ b/test/jdk/java/foreign/trivial/TestTrivialUpcall.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, 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. + * + * 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. + */ + +/* + * @test + * @enablePreview + * @library ../ /test/lib + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64" + * @requires vm.flavor != "zero" + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestTrivialUpcall + */ + +import org.testng.annotations.Test; + +import java.io.IOException; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; + +import static org.testng.Assert.fail; + +public class TestTrivialUpcall extends UpcallTestHelper { + + @Test + public void testUpcallFailure() throws IOException, InterruptedException { + // test to see if we catch a trivial downcall doing an upcall + runInNewProcess(Runner.class, true).assertStdOutContains("wrong thread state for upcall"); + } + + public static class Runner extends NativeTestHelper { + public static void main(String[] args) throws Throwable { + System.loadLibrary("Trivial"); + + MethodHandle mh = downcallHandle("do_upcall", FunctionDescriptor.ofVoid(C_POINTER), Linker.Option.isTrivial()); + MemorySegment stub = upcallStub(Runner.class, "target", FunctionDescriptor.ofVoid()); + mh.invokeExact(stub); + } + + public static void target() { + fail("Should not get here"); + } + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/libVaList.c b/test/jdk/java/foreign/trivial/libTrivial.c similarity index 69% rename from test/micro/org/openjdk/bench/java/lang/foreign/libVaList.c rename to test/jdk/java/foreign/trivial/libTrivial.c index 27f612bc70d..bc3159f417f 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/libVaList.c +++ b/test/jdk/java/foreign/trivial/libTrivial.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -21,7 +21,7 @@ * questions. */ -#include +#include #ifdef _WIN64 #define EXPORT __declspec(dllexport) @@ -29,13 +29,29 @@ #define EXPORT #endif -EXPORT void vaList(int argCount, va_list list) { - //... +EXPORT void empty() {} + +EXPORT int identity(int value) { + return value; } -EXPORT void ellipsis(int argCount, ...) { - va_list list; - va_start(list, argCount); - vaList(argCount, list); - va_end(list); +// 128 bit struct returned in buffer on SysV +struct Big { + long long x; + long long y; +}; + +EXPORT struct Big with_return_buffer() { + struct Big b; + b.x = 10; + b.y = 11; + return b; +} + +EXPORT void capture_errno(int value) { + errno = value; +} + +EXPORT void do_upcall(void(*f)(void)) { + f(); } diff --git a/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java b/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java index bfa61c5b266..504d8b9730c 100644 --- a/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java +++ b/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -79,8 +79,8 @@ public class TestUpcallDeopt extends NativeTestHelper { // we need to deoptimize through an uncommon trap in the callee of the optimized upcall stub // that is created when calling upcallStub below public static void main(String[] args) throws Throwable { - try (Arena arena = Arena.openConfined()) { - MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(C_INT, C_INT, C_INT, C_INT), arena.scope()); + try (Arena arena = Arena.ofConfined()) { + MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(C_INT, C_INT, C_INT, C_INT), arena); armed = false; for (int i = 0; i < 20_000; i++) { payload(stub); // warmup diff --git a/test/jdk/java/foreign/valist/VaListTest.java b/test/jdk/java/foreign/valist/VaListTest.java deleted file mode 100644 index 4afc3648c2b..00000000000 --- a/test/jdk/java/foreign/valist/VaListTest.java +++ /dev/null @@ -1,933 +0,0 @@ -/* - * Copyright (c) 2020, 2022, 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. - * - * 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. - * - */ - -/* - * @test - * @enablePreview - * @library ../ - * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64" - * @modules java.base/jdk.internal.foreign - * java.base/jdk.internal.foreign.abi - * java.base/jdk.internal.foreign.abi.x64 - * java.base/jdk.internal.foreign.abi.x64.sysv - * java.base/jdk.internal.foreign.abi.x64.windows - * java.base/jdk.internal.foreign.abi.aarch64 - * java.base/jdk.internal.foreign.abi.aarch64.linux - * java.base/jdk.internal.foreign.abi.aarch64.macos - * java.base/jdk.internal.foreign.abi.aarch64.windows - * java.base/jdk.internal.foreign.abi.riscv64 - * java.base/jdk.internal.foreign.abi.riscv64.linux - * @run testng/othervm --enable-native-access=ALL-UNNAMED VaListTest - */ - -import java.lang.foreign.*; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.VaList; -import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; -import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; -import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker; -import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; -import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandleProxies; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.invoke.VarHandle; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.DoubleStream; -import java.util.stream.IntStream; - -import static java.lang.foreign.MemoryLayout.PathElement.groupElement; -import static java.lang.foreign.ValueLayout.ADDRESS; -import static java.lang.foreign.ValueLayout.JAVA_DOUBLE; -import static java.lang.foreign.ValueLayout.JAVA_INT; -import static java.lang.foreign.ValueLayout.JAVA_LONG; -import static jdk.internal.foreign.PlatformLayouts.*; -import static org.testng.Assert.*; - -public class VaListTest extends NativeTestHelper { - - private static final Linker abi = Linker.nativeLinker(); - static { - System.loadLibrary("VaList"); - } - - private static final MethodHandle VALIST_TO_ADDRESS; - private static final MethodHandle SEGMENT_TO_VALIST; - - static { - try { - VALIST_TO_ADDRESS = MethodHandles.lookup().findVirtual(VaList.class, "segment", MethodType.methodType(MemorySegment.class)); - SEGMENT_TO_VALIST = MethodHandles.lookup().findStatic(VaListTest.class, "segmentToValist", MethodType.methodType(VaList.class, MemorySegment.class)); - } catch (Throwable ex) { - throw new ExceptionInInitializerError(ex); - } - } - - - private static final MethodHandle MH_sumInts = linkVaList("sumInts", - FunctionDescriptor.of(C_INT, C_INT, C_POINTER)); - private static final MethodHandle MH_sumDoubles = linkVaList("sumDoubles", - FunctionDescriptor.of(C_DOUBLE, C_INT, C_POINTER)); - private static final MethodHandle MH_getInt = linkVaList("getInt", - FunctionDescriptor.of(C_INT, C_POINTER)); - private static final MethodHandle MH_sumStruct = linkVaList("sumStruct", - FunctionDescriptor.of(C_INT, C_POINTER)); - private static final MethodHandle MH_sumBigStruct = linkVaList("sumBigStruct", - FunctionDescriptor.of(C_LONG_LONG, C_POINTER)); - private static final MethodHandle MH_sumHugeStruct = linkVaList("sumHugeStruct", - FunctionDescriptor.of(C_LONG_LONG, C_POINTER)); - private static final MethodHandle MH_sumFloatStruct = linkVaList("sumFloatStruct", - FunctionDescriptor.of(C_FLOAT, C_POINTER)); - private static final MethodHandle MH_sumStack = linkVaList("sumStack", - FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER)); - - private static MethodHandle link(String symbol, FunctionDescriptor fd) { - return linkInternal(symbol, fd); - } - - private static MethodHandle linkVaList(String symbol, FunctionDescriptor fd) { - return MethodHandles.filterArguments(linkInternal(symbol, fd), fd.argumentLayouts().size() - 1, VALIST_TO_ADDRESS); - } - - - private static MethodHandle linkInternal(String symbol, FunctionDescriptor fd) { - return abi.downcallHandle(findNativeOrThrow(symbol), fd); - } - - private static MethodHandle linkVaListCB(String symbol) { - return link(symbol, - FunctionDescriptor.ofVoid(C_POINTER)); - - } - - private static final Function, VaList> winVaListFactory - = actions -> Windowsx64Linker.newVaList(actions, SegmentScope.auto()); - private static final Function, VaList> sysvVaListFactory - = actions -> SysVx64Linker.newVaList(actions, SegmentScope.auto()); - private static final Function, VaList> linuxAArch64VaListFactory - = actions -> LinuxAArch64Linker.newVaList(actions, SegmentScope.auto()); - private static final Function, VaList> macAArch64VaListFactory - = actions -> MacOsAArch64Linker.newVaList(actions, SegmentScope.auto()); - private static final Function, VaList> linuxRISCV64VaListFactory - = actions -> LinuxRISCV64Linker.newVaList(actions, SegmentScope.auto()); - private static final Function, VaList> platformVaListFactory - = (builder) -> VaList.make(builder, SegmentScope.auto()); - - private static final BiFunction, SegmentScope, VaList> winVaListScopedFactory - = Windowsx64Linker::newVaList; - private static final BiFunction, SegmentScope, VaList> sysvVaListScopedFactory - = SysVx64Linker::newVaList; - private static final BiFunction, SegmentScope, VaList> linuxAArch64VaListScopedFactory - = LinuxAArch64Linker::newVaList; - private static final BiFunction, SegmentScope, VaList> macAArch64VaListScopedFactory - = MacOsAArch64Linker::newVaList; - private static final BiFunction, SegmentScope, VaList> linuxRISCV64VaListScopedFactory - = LinuxRISCV64Linker::newVaList; - private static final BiFunction, SegmentScope, VaList> platformVaListScopedFactory - = VaList::make; - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] sumInts() { - Function> sumIntsJavaFact = layout -> - (num, list) -> IntStream.generate(() -> list.nextVarg(layout)).limit(num).sum(); - BiFunction sumIntsNative - = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts); - return new Object[][]{ - { winVaListFactory, sumIntsJavaFact.apply(Win64.C_INT), Win64.C_INT }, - { sysvVaListFactory, sumIntsJavaFact.apply(SysV.C_INT), SysV.C_INT }, - { linuxAArch64VaListFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT }, - { macAArch64VaListFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT }, - { linuxRISCV64VaListFactory, sumIntsJavaFact.apply(RISCV64.C_INT), RISCV64.C_INT }, - { platformVaListFactory, sumIntsNative, C_INT }, - }; - } - - @Test(dataProvider = "sumInts") - public void testIntSum(Function, VaList> vaListFactory, - BiFunction sumInts, - ValueLayout.OfInt intLayout) { - VaList vaList = vaListFactory.apply(b -> - b.addVarg(intLayout, 10) - .addVarg(intLayout, 15) - .addVarg(intLayout, 20)); - int x = sumInts.apply(3, vaList); - assertEquals(x, 45); - } - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] sumDoubles() { - Function> sumDoublesJavaFact = layout -> - (num, list) -> DoubleStream.generate(() -> list.nextVarg(layout)).limit(num).sum(); - BiFunction sumDoublesNative - = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumDoubles); - return new Object[][]{ - { winVaListFactory, sumDoublesJavaFact.apply(Win64.C_DOUBLE), Win64.C_DOUBLE }, - { sysvVaListFactory, sumDoublesJavaFact.apply(SysV.C_DOUBLE), SysV.C_DOUBLE }, - { linuxAArch64VaListFactory, sumDoublesJavaFact.apply(AArch64.C_DOUBLE), AArch64.C_DOUBLE }, - { macAArch64VaListFactory, sumDoublesJavaFact.apply(AArch64.C_DOUBLE), AArch64.C_DOUBLE }, - { linuxRISCV64VaListFactory, sumDoublesJavaFact.apply(RISCV64.C_DOUBLE), RISCV64.C_DOUBLE }, - { platformVaListFactory, sumDoublesNative, C_DOUBLE }, - }; - } - - @Test(dataProvider = "sumDoubles") - public void testDoubleSum(Function, VaList> vaListFactory, - BiFunction sumDoubles, - ValueLayout.OfDouble doubleLayout) { - VaList vaList = vaListFactory.apply(b -> - b.addVarg(doubleLayout, 3.0D) - .addVarg(doubleLayout, 4.0D) - .addVarg(doubleLayout, 5.0D)); - double x = sumDoubles.apply(3, vaList); - assertEquals(x, 12.0D); - } - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] pointers() { - Function> getIntJavaFact = layout -> - list -> { - MemorySegment ma = list.nextVarg(layout); - return ma.get(JAVA_INT, 0); - }; - Function getIntNative = MethodHandleProxies.asInterfaceInstance(Function.class, MH_getInt); - return new Object[][]{ - { winVaListFactory, getIntJavaFact.apply(Win64.C_POINTER), Win64.C_POINTER }, - { sysvVaListFactory, getIntJavaFact.apply(SysV.C_POINTER), SysV.C_POINTER }, - { linuxAArch64VaListFactory, getIntJavaFact.apply(AArch64.C_POINTER), AArch64.C_POINTER }, - { macAArch64VaListFactory, getIntJavaFact.apply(AArch64.C_POINTER), AArch64.C_POINTER }, - { linuxRISCV64VaListFactory, getIntJavaFact.apply(RISCV64.C_POINTER), RISCV64.C_POINTER }, - { platformVaListFactory, getIntNative, C_POINTER }, - }; - } - - @Test(dataProvider = "pointers") - public void testVaListMemorySegment(Function, VaList> vaListFactory, - Function getFromPointer, - ValueLayout.OfAddress pointerLayout) { - try (Arena arena = Arena.openConfined()) { - MemorySegment msInt = MemorySegment.allocateNative(JAVA_INT, arena.scope());; - msInt.set(JAVA_INT, 0, 10); - VaList vaList = vaListFactory.apply(b -> b.addVarg(pointerLayout, msInt)); - int x = getFromPointer.apply(vaList); - assertEquals(x, 10); - } - } - - interface TriFunction { - R apply(S s, T t, U u); - } - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] structs() { - TriFunction> sumStructJavaFact - = (pointLayout, VH_Point_x, VH_Point_y) -> - list -> { - MemorySegment struct = MemorySegment.allocateNative(pointLayout, SegmentScope.auto()); - list.nextVarg(pointLayout, SegmentAllocator.prefixAllocator(struct)); - int x = (int) VH_Point_x.get(struct); - int y = (int) VH_Point_y.get(struct); - return x + y; - }; - - TriFunction> sumStructNativeFact - = (pointLayout, VH_Point_x, VH_Point_y) -> - MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumStruct); - - TriFunction, VaList>, MemoryLayout, - TriFunction>, Object[]> argsFact - = (vaListFact, intLayout, sumStructFact) -> { - GroupLayout pointLayout = MemoryLayout.structLayout( - intLayout.withName("x"), - intLayout.withName("y") - ); - VarHandle VH_Point_x = pointLayout.varHandle(groupElement("x")); - VarHandle VH_Point_y = pointLayout.varHandle(groupElement("y")); - return new Object[] { vaListFact, sumStructFact.apply(pointLayout, VH_Point_x, VH_Point_y), - pointLayout, VH_Point_x, VH_Point_y }; - }; - return new Object[][]{ - argsFact.apply(winVaListFactory, Win64.C_INT, sumStructJavaFact), - argsFact.apply(sysvVaListFactory, SysV.C_INT, sumStructJavaFact), - argsFact.apply(linuxAArch64VaListFactory, AArch64.C_INT, sumStructJavaFact), - argsFact.apply(macAArch64VaListFactory, AArch64.C_INT, sumStructJavaFact), - argsFact.apply(linuxRISCV64VaListFactory, RISCV64.C_INT, sumStructJavaFact), - argsFact.apply(platformVaListFactory, C_INT, sumStructNativeFact), - }; - } - - @Test(dataProvider = "structs") - public void testStruct(Function, VaList> vaListFactory, - Function sumStruct, - GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) { - try (Arena arena = Arena.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, arena.scope());; - VH_Point_x.set(struct, 5); - VH_Point_y.set(struct, 10); - - VaList vaList = vaListFactory.apply(b -> b.addVarg(Point_LAYOUT, struct)); - int sum = sumStruct.apply(vaList); - assertEquals(sum, 15); - } - } - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] bigStructs() { - TriFunction> sumStructJavaFact - = (BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y) -> - list -> { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); - list.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - long x = (long) VH_BigPoint_x.get(struct); - long y = (long) VH_BigPoint_y.get(struct); - return x + y; - }; - - TriFunction> sumStructNativeFact - = (pointLayout, VH_BigPoint_x, VH_BigPoint_y) -> - MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumBigStruct); - - TriFunction, VaList>, MemoryLayout, - TriFunction>, Object[]> argsFact - = (vaListFact, longLongLayout, sumBigStructFact) -> { - GroupLayout BigPoint_LAYOUT = MemoryLayout.structLayout( - longLongLayout.withName("x"), - longLongLayout.withName("y") - ); - VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(groupElement("x")); - VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(groupElement("y")); - return new Object[] { vaListFact, sumBigStructFact.apply(BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y), - BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y }; - }; - return new Object[][]{ - argsFact.apply(winVaListFactory, Win64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(sysvVaListFactory, SysV.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(linuxAArch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(macAArch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(linuxRISCV64VaListFactory, RISCV64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(platformVaListFactory, C_LONG_LONG, sumStructNativeFact), - }; - } - - @Test(dataProvider = "bigStructs") - public void testBigStruct(Function, VaList> vaListFactory, - Function sumBigStruct, - GroupLayout BigPoint_LAYOUT, VarHandle VH_BigPoint_x, VarHandle VH_BigPoint_y) { - try (Arena arena = Arena.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, arena.scope());; - VH_BigPoint_x.set(struct, 5); - VH_BigPoint_y.set(struct, 10); - - VaList vaList = vaListFactory.apply(b -> b.addVarg(BigPoint_LAYOUT, struct)); - long sum = sumBigStruct.apply(vaList); - assertEquals(sum, 15); - } - } - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] floatStructs() { - TriFunction> sumStructJavaFact - = (FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y) -> - list -> { - MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, SegmentScope.auto()); - list.nextVarg(FloatPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - float x = (float) VH_FloatPoint_x.get(struct); - float y = (float) VH_FloatPoint_y.get(struct); - return x + y; - }; - - TriFunction> sumStructNativeFact - = (pointLayout, VH_FloatPoint_x, VH_FloatPoint_y) -> - MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumFloatStruct); - - TriFunction, VaList>, MemoryLayout, - TriFunction>, Object[]> argsFact - = (vaListFact, floatLayout, sumFloatStructFact) -> { - GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout( - floatLayout.withName("x"), - floatLayout.withName("y") - ); - VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(groupElement("x")); - VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(groupElement("y")); - return new Object[] { vaListFact, sumFloatStructFact.apply(FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y), - FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y }; - }; - return new Object[][]{ - argsFact.apply(winVaListFactory, Win64.C_FLOAT, sumStructJavaFact), - argsFact.apply(sysvVaListFactory, SysV.C_FLOAT, sumStructJavaFact), - argsFact.apply(linuxAArch64VaListFactory, AArch64.C_FLOAT, sumStructJavaFact), - argsFact.apply(macAArch64VaListFactory, AArch64.C_FLOAT, sumStructJavaFact), - argsFact.apply(linuxRISCV64VaListFactory, RISCV64.C_FLOAT, sumStructJavaFact), - argsFact.apply(platformVaListFactory, C_FLOAT, sumStructNativeFact), - }; - } - - @Test(dataProvider = "floatStructs") - public void testFloatStruct(Function, VaList> vaListFactory, - Function sumFloatStruct, - GroupLayout FloatPoint_LAYOUT, - VarHandle VH_FloatPoint_x, VarHandle VH_FloatPoint_y) { - try (Arena arena = Arena.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, arena.scope());; - VH_FloatPoint_x.set(struct, 1.234f); - VH_FloatPoint_y.set(struct, 3.142f); - - VaList vaList = vaListFactory.apply(b -> b.addVarg(FloatPoint_LAYOUT, struct)); - float sum = sumFloatStruct.apply(vaList); - assertEquals(sum, 4.376f, 0.00001f); - } - } - - interface QuadFunc { - R apply(T0 t0, T1 t1, T2 t2, T3 t3); - } - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] hugeStructs() { - QuadFunc> sumStructJavaFact - = (HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) -> - list -> { - MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, SegmentScope.auto()); - list.nextVarg(HugePoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - long x = (long) VH_HugePoint_x.get(struct); - long y = (long) VH_HugePoint_y.get(struct); - long z = (long) VH_HugePoint_z.get(struct); - return x + y + z; - }; - - QuadFunc> sumStructNativeFact - = (pointLayout, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) -> - MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumHugeStruct); - - TriFunction, VaList>, MemoryLayout, - QuadFunc>, Object[]> argsFact - = (vaListFact, longLongLayout, sumBigStructFact) -> { - GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout( - longLongLayout.withName("x"), - longLongLayout.withName("y"), - longLongLayout.withName("z") - ); - VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(groupElement("x")); - VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(groupElement("y")); - VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(groupElement("z")); - return new Object[] { vaListFact, - sumBigStructFact.apply(HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z), - HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z }; - }; - return new Object[][]{ - argsFact.apply(winVaListFactory, Win64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(sysvVaListFactory, SysV.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(linuxAArch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(macAArch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(linuxRISCV64VaListFactory, RISCV64.C_LONG_LONG, sumStructJavaFact), - argsFact.apply(platformVaListFactory, C_LONG_LONG, sumStructNativeFact), - }; - } - - @Test(dataProvider = "hugeStructs") - public void testHugeStruct(Function, VaList> vaListFactory, - Function sumHugeStruct, - GroupLayout HugePoint_LAYOUT, - VarHandle VH_HugePoint_x, VarHandle VH_HugePoint_y, VarHandle VH_HugePoint_z) { - // On AArch64 a struct needs to be larger than 16 bytes to be - // passed by reference. - try (Arena arena = Arena.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, arena.scope());; - VH_HugePoint_x.set(struct, 1); - VH_HugePoint_y.set(struct, 2); - VH_HugePoint_z.set(struct, 3); - - VaList vaList = vaListFactory.apply(b -> b.addVarg(HugePoint_LAYOUT, struct)); - long sum = sumHugeStruct.apply(vaList); - assertEquals(sum, 6); - } - } - - public interface SumStackFunc { - void invoke(MemorySegment longSum, MemorySegment doubleSum, VaList list); - } - - @DataProvider - public static Object[][] sumStack() { - BiFunction sumStackJavaFact = (longLayout, doubleLayout) -> - (longSum, doubleSum, list) -> { - long lSum = 0L; - for (int i = 0; i < 16; i++) { - lSum += list.nextVarg(longLayout); - } - longSum.set(JAVA_LONG, 0, lSum); - double dSum = 0D; - for (int i = 0; i < 16; i++) { - dSum += list.nextVarg(doubleLayout); - } - doubleSum.set(JAVA_DOUBLE, 0, dSum); - }; - SumStackFunc sumStackNative = (longSum, doubleSum, list) -> { - try { - MH_sumStack.invokeExact(longSum, doubleSum, list); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - }; - return new Object[][]{ - { winVaListFactory, sumStackJavaFact.apply(Win64.C_LONG_LONG, Win64.C_DOUBLE), Win64.C_LONG_LONG, Win64.C_DOUBLE }, - { sysvVaListFactory, sumStackJavaFact.apply(SysV.C_LONG_LONG, SysV.C_DOUBLE), SysV.C_LONG_LONG, SysV.C_DOUBLE }, - { linuxAArch64VaListFactory, sumStackJavaFact.apply(AArch64.C_LONG_LONG, AArch64.C_DOUBLE), AArch64.C_LONG_LONG, AArch64.C_DOUBLE }, - { macAArch64VaListFactory, sumStackJavaFact.apply(AArch64.C_LONG_LONG, AArch64.C_DOUBLE), AArch64.C_LONG_LONG, AArch64.C_DOUBLE }, - { linuxRISCV64VaListFactory, sumStackJavaFact.apply(RISCV64.C_LONG_LONG, RISCV64.C_DOUBLE), RISCV64.C_LONG_LONG, RISCV64.C_DOUBLE }, - { platformVaListFactory, sumStackNative, C_LONG_LONG, C_DOUBLE }, - }; - } - - @Test(dataProvider = "sumStack") - public void testStack(Function, VaList> vaListFactory, - SumStackFunc sumStack, - ValueLayout.OfLong longLayout, - ValueLayout.OfDouble doubleLayout) { - try (Arena arena = Arena.openConfined()) { - MemorySegment longSum = MemorySegment.allocateNative(longLayout, arena.scope());; - MemorySegment doubleSum = MemorySegment.allocateNative(doubleLayout, arena.scope());; - longSum.set(JAVA_LONG, 0, 0L); - doubleSum.set(JAVA_DOUBLE, 0, 0D); - - VaList list = vaListFactory.apply(b -> { - for (long l = 1; l <= 16L; l++) { - b.addVarg(longLayout, l); - } - for (double d = 1; d <= 16D; d++) { - b.addVarg(doubleLayout, d); - } - }); - - sumStack.invoke(longSum, doubleSum, list); - - long lSum = longSum.get(JAVA_LONG, 0); - double dSum = doubleSum.get(JAVA_DOUBLE, 0); - - assertEquals(lSum, 136L); - assertEquals(dSum, 136D); - } - } - - @Test(dataProvider = "upcalls") - public void testUpcall(MethodHandle target, MethodHandle callback) throws Throwable { - FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_POINTER); - try (Arena arena = Arena.openConfined()) { - MemorySegment stub = abi.upcallStub(callback, desc, arena.scope()); - target.invokeExact(stub); - } - } - - @DataProvider - public Object[][] emptyVaLists() { - return new Object[][] { - { Windowsx64Linker.emptyVaList() }, - { winVaListFactory.apply(b -> {}) }, - { SysVx64Linker.emptyVaList() }, - { sysvVaListFactory.apply(b -> {}) }, - { LinuxAArch64Linker.emptyVaList() }, - { linuxAArch64VaListFactory.apply(b -> {}) }, - { MacOsAArch64Linker.emptyVaList() }, - { macAArch64VaListFactory.apply(b -> {}) }, - { LinuxRISCV64Linker.emptyVaList() }, - { linuxRISCV64VaListFactory.apply(b -> {}) }, - }; - } - - @DataProvider - @SuppressWarnings("unchecked") - public static Object[][] sumIntsScoped() { - Function> sumIntsJavaFact = layout -> - (num, list) -> IntStream.generate(() -> list.nextVarg(layout)).limit(num).sum(); - BiFunction sumIntsNative - = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts); - return new Object[][]{ - { winVaListScopedFactory, sumIntsJavaFact.apply(Win64.C_INT), Win64.C_INT }, - { sysvVaListScopedFactory, sumIntsJavaFact.apply(SysV.C_INT), SysV.C_INT }, - { linuxAArch64VaListScopedFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT }, - { macAArch64VaListScopedFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT }, - { linuxRISCV64VaListScopedFactory, sumIntsJavaFact.apply(RISCV64.C_INT), RISCV64.C_INT }, - { platformVaListScopedFactory, sumIntsNative, C_INT }, - }; - } - - @Test(dataProvider = "sumIntsScoped") - public void testScopedVaList(BiFunction, SegmentScope, VaList> vaListFactory, - BiFunction sumInts, - ValueLayout.OfInt intLayout) { - VaList listLeaked; - try (Arena arena = Arena.openConfined()) { - VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) - .addVarg(intLayout, 8), arena.scope()); - int x = sumInts.apply(2, list); - assertEquals(x, 12); - listLeaked = list; - } - assertFalse(listLeaked.segment().scope().isAlive()); - } - - @Test(dataProvider = "structs") - public void testScopeMSRead(Function, VaList> vaListFactory, - Function sumStruct, // ignored - GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) { - MemorySegment pointOut; - try (Arena arena = Arena.openConfined()) { - try (Arena innerArena = Arena.openConfined()) { - MemorySegment pointIn = MemorySegment.allocateNative(Point_LAYOUT, innerArena.scope());; - VH_Point_x.set(pointIn, 3); - VH_Point_y.set(pointIn, 6); - VaList list = vaListFactory.apply(b -> b.addVarg(Point_LAYOUT, pointIn)); - pointOut = MemorySegment.allocateNative(Point_LAYOUT, arena.scope());; - list.nextVarg(Point_LAYOUT, SegmentAllocator.prefixAllocator(pointOut)); - assertEquals((int) VH_Point_x.get(pointOut), 3); - assertEquals((int) VH_Point_y.get(pointOut), 6); - assertTrue(pointOut.scope().isAlive()); // after VaList freed - } - assertTrue(pointOut.scope().isAlive()); // after inner session freed - } - assertFalse(pointOut.scope().isAlive()); // after outer session freed - } - - @DataProvider - public Object[][] copy() { - return new Object[][] { - { winVaListScopedFactory, Win64.C_INT }, - { sysvVaListScopedFactory, SysV.C_INT }, - { linuxAArch64VaListScopedFactory, AArch64.C_INT }, - { macAArch64VaListScopedFactory, AArch64.C_INT }, - { linuxRISCV64VaListScopedFactory, RISCV64.C_INT }, - }; - } - - @Test(dataProvider = "copy") - public void testCopy(BiFunction, SegmentScope, VaList> vaListFactory, ValueLayout.OfInt intLayout) { - try (var arena = Arena.openConfined()) { - VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) - .addVarg(intLayout, 8), arena.scope()); - VaList copy = list.copy(); - assertEquals(copy.nextVarg(intLayout), 4); - assertEquals(copy.nextVarg(intLayout), 8); - - // try { // this logic only works on Windows! - // int x = copy.vargAsInt(intLayout); - // fail(); - // } catch (IndexOutOfBoundsException ex) { - // // ok - we exhausted the list - // } - - assertEquals(list.nextVarg(intLayout), 4); - assertEquals(list.nextVarg(intLayout), 8); - } - } - - @Test(dataProvider = "copy", - expectedExceptions = IllegalStateException.class) - public void testCopyUnusableAfterOriginalClosed(BiFunction, SegmentScope, VaList> vaListFactory, - ValueLayout.OfInt intLayout) { - VaList copy; - try (var arena = Arena.openConfined()) { - VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) - .addVarg(intLayout, 8), arena.scope()); - copy = list.copy(); - } - - copy.nextVarg(intLayout); // should throw - } - - @DataProvider - public static Object[][] upcalls() { - GroupLayout BigPoint_LAYOUT = MemoryLayout.structLayout( - C_LONG_LONG.withName("x"), - C_LONG_LONG.withName("y") - ); - VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(groupElement("x")); - VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(groupElement("y")); - GroupLayout Point_LAYOUT = MemoryLayout.structLayout( - C_INT.withName("x"), - C_INT.withName("y") - ); - VarHandle VH_Point_x = Point_LAYOUT.varHandle(groupElement("x")); - VarHandle VH_Point_y = Point_LAYOUT.varHandle(groupElement("y")); - GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout( - C_FLOAT.withName("x"), - C_FLOAT.withName("y") - ); - VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(groupElement("x")); - VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(groupElement("y")); - GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout( - C_LONG_LONG.withName("x"), - C_LONG_LONG.withName("y"), - C_LONG_LONG.withName("z") - ); - VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(groupElement("x")); - VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(groupElement("y")); - VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(groupElement("z")); - - return new Object[][]{ - { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); - vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - assertEquals((long) VH_BigPoint_x.get(struct), 8); - assertEquals((long) VH_BigPoint_y.get(struct), 16); - })}, - { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> { - VaList copy = vaList.copy(); - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); - vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - assertEquals((long) VH_BigPoint_x.get(struct), 8); - assertEquals((long) VH_BigPoint_y.get(struct), 16); - - VH_BigPoint_x.set(struct, 0); - VH_BigPoint_y.set(struct, 0); - - // should be independent - copy.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - assertEquals((long) VH_BigPoint_x.get(struct), 8); - assertEquals((long) VH_BigPoint_y.get(struct), 16); - })}, - { linkVaListCB("upcallBigStructPlusScalar"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); - vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - assertEquals((long) VH_BigPoint_x.get(struct), 8); - assertEquals((long) VH_BigPoint_y.get(struct), 16); - - assertEquals(vaList.nextVarg(C_LONG_LONG), 42); - })}, - { linkVaListCB("upcallBigStructPlusScalar"), VaListConsumer.mh(vaList -> { - vaList.skip(BigPoint_LAYOUT); - assertEquals(vaList.nextVarg(C_LONG_LONG), 42); - })}, - { linkVaListCB("upcallStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, SegmentScope.auto()); - vaList.nextVarg(Point_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - assertEquals((int) VH_Point_x.get(struct), 5); - assertEquals((int) VH_Point_y.get(struct), 10); - })}, - { linkVaListCB("upcallHugeStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, SegmentScope.auto()); - vaList.nextVarg(HugePoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - assertEquals((long) VH_HugePoint_x.get(struct), 1); - assertEquals((long) VH_HugePoint_y.get(struct), 2); - assertEquals((long) VH_HugePoint_z.get(struct), 3); - })}, - { linkVaListCB("upcallFloatStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, SegmentScope.auto()); - vaList.nextVarg(FloatPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); - assertEquals((float) VH_FloatPoint_x.get(struct), 1.0f); - assertEquals((float) VH_FloatPoint_y.get(struct), 2.0f); - })}, - { linkVaListCB("upcallMemoryAddress"), VaListConsumer.mh(vaList -> { - MemorySegment intPtr = vaList.nextVarg(C_POINTER); - int x = intPtr.get(JAVA_INT, 0); - assertEquals(x, 10); - })}, - { linkVaListCB("upcallDoubles"), VaListConsumer.mh(vaList -> { - assertEquals(vaList.nextVarg(C_DOUBLE), 3.0); - assertEquals(vaList.nextVarg(C_DOUBLE), 4.0); - assertEquals(vaList.nextVarg(C_DOUBLE), 5.0); - })}, - { linkVaListCB("upcallInts"), VaListConsumer.mh(vaList -> { - assertEquals(vaList.nextVarg(C_INT), 10); - assertEquals(vaList.nextVarg(C_INT), 15); - assertEquals(vaList.nextVarg(C_INT), 20); - })}, - { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> { - // skip all registers - for (long l = 1; l <= 16; l++) { - assertEquals(vaList.nextVarg(C_LONG_LONG), l); - } - for (double d = 1; d <= 16; d++) { - assertEquals(vaList.nextVarg(C_DOUBLE), d); - } - - // test some arbitrary values on the stack - assertEquals((byte) vaList.nextVarg(C_INT), (byte) 1); - assertEquals((char) vaList.nextVarg(C_INT), 'a'); - assertEquals((short) vaList.nextVarg(C_INT), (short) 3); - assertEquals(vaList.nextVarg(C_INT), 4); - assertEquals(vaList.nextVarg(C_LONG_LONG), 5L); - assertEquals((float) vaList.nextVarg(C_DOUBLE), 6.0F); - assertEquals(vaList.nextVarg(C_DOUBLE), 7.0D); - assertEquals((byte) vaList.nextVarg(C_INT), (byte) 8); - assertEquals((char) vaList.nextVarg(C_INT), 'b'); - assertEquals((short) vaList.nextVarg(C_INT), (short) 10); - assertEquals(vaList.nextVarg(C_INT), 11); - assertEquals(vaList.nextVarg(C_LONG_LONG), 12L); - assertEquals((float) vaList.nextVarg(C_DOUBLE), 13.0F); - assertEquals(vaList.nextVarg(C_DOUBLE), 14.0D); - - MemorySegment buffer = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); - SegmentAllocator bufferAllocator = SegmentAllocator.prefixAllocator(buffer); - - MemorySegment point = vaList.nextVarg(Point_LAYOUT, bufferAllocator); - assertEquals((int) VH_Point_x.get(point), 5); - assertEquals((int) VH_Point_y.get(point), 10); - - VaList copy = vaList.copy(); - MemorySegment bigPoint = vaList.nextVarg(BigPoint_LAYOUT, bufferAllocator); - assertEquals((long) VH_BigPoint_x.get(bigPoint), 15); - assertEquals((long) VH_BigPoint_y.get(bigPoint), 20); - - VH_BigPoint_x.set(bigPoint, 0); - VH_BigPoint_y.set(bigPoint, 0); - - // should be independent - MemorySegment struct = copy.nextVarg(BigPoint_LAYOUT, bufferAllocator); - assertEquals((long) VH_BigPoint_x.get(struct), 15); - assertEquals((long) VH_BigPoint_y.get(struct), 20); - })}, - // test skip - { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> { - vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG); - assertEquals(vaList.nextVarg(C_LONG_LONG), 5L); - vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG); - assertEquals(vaList.nextVarg(C_LONG_LONG), 10L); - vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG); - assertEquals(vaList.nextVarg(C_DOUBLE), 1.0D); - vaList.skip(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE); - assertEquals(vaList.nextVarg(C_DOUBLE), 6.0D); - })}, - }; - } - - interface VaListConsumer { - void accept(VaList list); - - static MethodHandle mh(VaListConsumer instance) { - try { - MethodHandle handle = MethodHandles.lookup().findVirtual(VaListConsumer.class, "accept", - MethodType.methodType(void.class, VaList.class)).bindTo(instance); - return MethodHandles.filterArguments(handle, 0, - SEGMENT_TO_VALIST); - } catch (ReflectiveOperationException e) { - throw new InternalError(e); - } - } - } - - static VaList segmentToValist(MemorySegment segment) { - return VaList.ofAddress(segment.address(), SegmentScope.auto()); - } - - @DataProvider - public static Object[][] overflow() { - List, VaList>> factories = List.of( - winVaListFactory, - sysvVaListFactory, - linuxAArch64VaListFactory, - macAArch64VaListFactory - ); - List> contentsCases = List.of( - List.of(JAVA_INT), - List.of(JAVA_LONG), - List.of(JAVA_DOUBLE), - List.of(ADDRESS), - List.of(JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, - JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, - JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, - JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, - JAVA_INT, JAVA_LONG, JAVA_DOUBLE, ADDRESS) - ); - List overflowCases = List.of( - JAVA_INT, - JAVA_LONG, - JAVA_DOUBLE, - ADDRESS - ); - return factories.stream() - .mapMulti((factory, sink) -> { - for (List content : contentsCases) { - for (MemoryLayout overflow : overflowCases) { - sink.accept(new Object[]{ factory, content, overflow }); - } - } - }) - .toArray(Object[][]::new); - } - - private static void buildVaList(VaList.Builder builder, List contents) { - for (MemoryLayout layout : contents) { - if (layout instanceof ValueLayout.OfInt ofInt) { - builder.addVarg(ofInt, 1); - } else if (layout instanceof ValueLayout.OfLong ofLong) { - builder.addVarg(ofLong, 1L); - } else if (layout instanceof ValueLayout.OfDouble ofDouble) { - builder.addVarg(ofDouble, 1D); - } else if (layout instanceof ValueLayout.OfAddress ofAddress) { - builder.addVarg(ofAddress, MemorySegment.ofAddress(1)); - } - } - } - - @Test(dataProvider = "overflow") - public void testSkipOverflow(Function, VaList> vaListFactory, - List contents, - MemoryLayout skipped) { - VaList vaList = vaListFactory.apply(b -> buildVaList(b, contents)); - vaList.skip(contents.toArray(MemoryLayout[]::new)); - assertThrows(NoSuchElementException.class, () -> vaList.skip(skipped)); - } - - private static void nextVarg(VaList vaList, MemoryLayout layout) { - if (layout instanceof ValueLayout.OfInt ofInt) { - assertEquals(vaList.nextVarg(ofInt), 1); - } else if (layout instanceof ValueLayout.OfLong ofLong) { - assertEquals(vaList.nextVarg(ofLong), 1L); - } else if (layout instanceof ValueLayout.OfDouble ofDouble) { - assertEquals(vaList.nextVarg(ofDouble), 1D); - } else if (layout instanceof ValueLayout.OfAddress ofAddress) { - assertEquals(vaList.nextVarg(ofAddress), MemorySegment.ofAddress(1)); - } - } - - @Test(dataProvider = "overflow") - public void testVargOverflow(Function, VaList> vaListFactory, - List contents, - MemoryLayout next) { - VaList vaList = vaListFactory.apply(b -> buildVaList(b, contents)); - for (MemoryLayout layout : contents) { - nextVarg(vaList, layout); - } - assertThrows(NoSuchElementException.class, () -> nextVarg(vaList, next)); - } - - @Test(dataProvider = "emptyVaLists") - public void testEmptyVaListScope(VaList vaList) { - assertEquals(vaList.segment().scope(), SegmentScope.global()); - } -} diff --git a/test/jdk/java/foreign/valist/libVaList.c b/test/jdk/java/foreign/valist/libVaList.c deleted file mode 100644 index 3311f6f70c7..00000000000 --- a/test/jdk/java/foreign/valist/libVaList.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2020, 2021, 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. - * - * 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. - * - */ - -#include - -#ifdef _WIN64 -#define EXPORT __declspec(dllexport) -#else -#define EXPORT -#endif - -// ###### Down calls - -EXPORT int sumInts(int argNum, va_list list) { - int sum = 0; - for (int i = 0; i < argNum; i++) { - sum += va_arg(list, int); - } - return sum; -} - -EXPORT double sumDoubles(int argNum, va_list list) { - double sum = 0; - for (int i = 0; i < argNum; i++) { - sum += va_arg(list, double); - } - return sum; -} - -EXPORT int getInt(va_list list) { - int* ptr = va_arg(list, int*); - return *ptr; -} - -typedef struct { - int x; - int y; -} Point; - -EXPORT int sumStruct(va_list list) { - Point point = va_arg(list, Point); - return point.x + point.y; -} - -typedef struct { - long long x; - long long y; -} BigPoint; - -EXPORT long long sumBigStruct(va_list list) { - BigPoint point = va_arg(list, BigPoint); - return point.x + point.y; -} - -typedef struct { - long long x; - long long y; - long long z; -} HugePoint; - -EXPORT long long sumHugeStruct(va_list list) { - HugePoint point = va_arg(list, HugePoint); - return point.x + point.y + point.z; -} - -typedef struct { - float x; - float y; -} FloatPoint; - -EXPORT float sumFloatStruct(va_list list) { - FloatPoint point = va_arg(list, FloatPoint); - return point.x + point.y; -} - -EXPORT void sumStack(long long* longSum, double* doubleSum, va_list list) { - long long lSum = 0; - for (int i = 0; i < 16; i++) { - lSum += va_arg(list, long long); - } - *longSum = lSum; - double dSum = 0.0; - for (int i = 0; i < 16; i++) { - dSum += va_arg(list, double); - } - *doubleSum = dSum; -} - -// ###### Up calls - -typedef void CB(va_list); - -static void passToUpcall(CB cb, int numArgs, ...) { - va_list list; - va_start(list, numArgs); - cb(list); - va_end(list); -} - -EXPORT void upcallInts(CB cb) { - passToUpcall(cb, 3, 10, 15, 20); -} - -EXPORT void upcallDoubles(CB cb) { - passToUpcall(cb, 3, 3.0, 4.0, 5.0); -} - -EXPORT void upcallStack(CB cb) { - Point point; - point.x = 5; - point.y = 10; - - BigPoint bigPoint; - bigPoint.x = 15; - bigPoint.y = 20; - - passToUpcall(cb, 32 + 14, - 1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, - 9LL, 10LL, 11LL, 12LL, 13LL, 14LL, 15LL, 16LL, - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, - 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, - // should all be passed on the stack - 1, 'a', 3, 4, 5LL, 6.0f, 7.0, - 8, 'b', 10, 11, 12LL, 13.0f, 14.0, - point, bigPoint); -} - -EXPORT void upcallMemoryAddress(CB cb) { - int x = 10; - passToUpcall(cb, 1, &x); -} - -EXPORT void upcallStruct(CB cb) { - Point point; - point.x = 5; - point.y = 10; - passToUpcall(cb, 1, point); -} - -EXPORT void upcallFloatStruct(CB cb) { - FloatPoint point; - point.x = 1.0f; - point.y = 2.0f; - passToUpcall(cb, 1, point); -} - -EXPORT void upcallBigStruct(CB cb) { - BigPoint point; - point.x = 8; - point.y = 16; - passToUpcall(cb, 1, point); -} - -EXPORT void upcallBigStructPlusScalar(CB cb) { - BigPoint point; - point.x = 8; - point.y = 16; - passToUpcall(cb, 2, point, 42); -} - -EXPORT void upcallHugeStruct(CB cb) { - HugePoint point; - point.x = 1; - point.y = 2; - point.z = 3; - passToUpcall(cb, 1, point); -} diff --git a/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java b/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java index 5e676da446b..df975dba184 100644 --- a/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java +++ b/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -32,7 +32,7 @@ import java.util.concurrent.CountDownLatch; */ public class ImplicitAttach { private static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); - private static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); + private static final AddressLayout C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); private static volatile CountDownLatch latch; @@ -58,7 +58,7 @@ public class ImplicitAttach { .findStatic(ImplicitAttach.class, "callback", MethodType.methodType(void.class)); MemorySegment upcallStub = abi.upcallStub(callback, FunctionDescriptor.ofVoid(), - SegmentScope.global()); + Arena.global()); // void start_threads(int count, void *(*f)(void *)) SymbolLookup symbolLookup = SymbolLookup.loaderLookup(); diff --git a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java index 8241b440031..65329a4011a 100644 --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,6 +25,7 @@ * @test * @enablePreview * @modules java.base/jdk.internal.access.foreign + * @modules java.base/jdk.internal.foreign.layout * * @run testng/othervm -Xverify:all * -Djdk.internal.foreign.SHOULD_ADAPT_HANDLES=false @@ -47,8 +48,9 @@ */ import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; + +import jdk.internal.foreign.layout.ValueLayouts; import org.testng.SkipException; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -169,8 +171,8 @@ public class VarHandleTestExact { @Test(dataProvider = "dataSetMemorySegment") public void testExactSegmentSet(Class carrier, Object testValue, SetSegmentX setter) { - VarHandle vh = MethodHandles.memorySegmentViewVarHandle(MemoryLayout.valueLayout(carrier, ByteOrder.nativeOrder())); - try (Arena arena = Arena.openConfined()) { + VarHandle vh = MethodHandles.memorySegmentViewVarHandle(ValueLayouts.valueLayout(carrier, ByteOrder.nativeOrder())); + try (Arena arena = Arena.ofConfined()) { MemorySegment seg = arena.allocate(8); doTest(vh, tvh -> tvh.set(seg, 0L, testValue), diff --git a/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java b/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java index afd067605ae..0597f2f8332 100644 --- a/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java +++ b/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java @@ -23,8 +23,8 @@ import jdk.test.lib.RandomFactory; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.io.IOException; import java.nio.ByteBuffer; @@ -78,7 +78,7 @@ public class LargeMapTest { try (FileChannel fc = FileChannel.open(p, READ, WRITE)) { MemorySegment mappedMemorySegment = fc.map(FileChannel.MapMode.READ_WRITE, 0, p.toFile().length(), - SegmentScope.auto()); + Arena.ofAuto()); MemorySegment target = mappedMemorySegment.asSlice(BASE, EXTRA); if (!target.asByteBuffer().equals(bb)) { throw new RuntimeException("Expected buffers to be equal"); diff --git a/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java b/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java index b06af75c48d..f46e9116d97 100644 --- a/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java +++ b/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java @@ -32,7 +32,6 @@ import java.io.File; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -62,21 +61,21 @@ public class MapToMemorySegmentTest { @Test(expectedExceptions = UnsupportedOperationException.class) public void testCustomFileChannel() throws IOException { - var arena = Arena.openConfined(); + var arena = Arena.ofConfined(); var fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ); var fileChannel = new CustomFileChannel(fc); try (arena; fileChannel){ - fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena.scope()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena); } } @Test public void testCustomFileChannelOverride() throws IOException { - var arena = Arena.openConfined(); + var arena = Arena.ofConfined(); var fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ); var fileChannel = new CustomFileChannelOverride(fc); try (arena; fileChannel){ - fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena.scope()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena); } } @@ -161,10 +160,10 @@ public class MapToMemorySegmentTest { public CustomFileChannelOverride(FileChannel fc) { super(fc); } @Override - public MemorySegment map(MapMode mode, long offset, long size, SegmentScope scope) + public MemorySegment map(MapMode mode, long offset, long size, Arena arena) throws IOException, UnsupportedOperationException { - return fc.map(mode, offset, size, scope); + return fc.map(mode, offset, size, arena); } } } diff --git a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java index efe69ef3c36..ab0b037fda0 100644 --- a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java +++ b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java @@ -65,7 +65,7 @@ public class SpliteratorTest { @Test(dataProvider = "SegmentSpliterator", dataProviderClass = SegmentTestDataProvider.class ) public void testSegmentSpliterator(String name, SequenceLayout layout, SpliteratorTestHelper.ContentAsserter contentAsserter) { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(layout); SegmentTestDataProvider.initSegment(segment); SpliteratorTestHelper.testSpliterator(() -> segment.spliterator(layout), diff --git a/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java b/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java index 8139931f41d..20eaf638909 100644 --- a/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java +++ b/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java @@ -21,8 +21,8 @@ * questions. */ +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Collection; @@ -43,7 +43,7 @@ public class AbstractVectorLoadStoreTest extends AbstractVectorTest { ByteBuffer.allocateDirect(s) .order(ByteOrder.nativeOrder())), withToString("MS:RW:NE", (int s) -> - MemorySegment.allocateNative(s, SegmentScope.auto()) + Arena.ofAuto().allocate(s) .asByteBuffer() .order(ByteOrder.nativeOrder()) ) @@ -51,7 +51,7 @@ public class AbstractVectorLoadStoreTest extends AbstractVectorTest { static final List> MEMORY_SEGMENT_GENERATORS = List.of( withToString("HMS", (int s) -> - MemorySegment.allocateNative(s, SegmentScope.auto()) + Arena.ofAuto().allocate(s) ), withToString("DMS", (int s) -> { byte[] b = new byte[s]; diff --git a/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java index c588b03fb68..1b1cb9cbf51 100644 --- a/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Byte128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Byte128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Byte128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Byte128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java index 0a60dc2742c..cc6d863e4d5 100644 --- a/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Byte256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Byte256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Byte256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Byte256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java index 807a1647532..dd2ed1960e9 100644 --- a/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Byte512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Byte512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Byte512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Byte512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java index 1274106caff..7fbff2fc2c8 100644 --- a/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Byte64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Byte64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Byte64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Byte64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java index 1d956bc339c..ae5be9e8aac 100644 --- a/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ public class ByteMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ public class ByteMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ public class ByteMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ public class ByteMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Byte.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Byte.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java index 4ff63cf1fda..0cc14e2ad22 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Double128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Double128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Double128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Double128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java index ef3ab3d5b26..58e5f4f6f3a 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Double256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Double256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Double256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Double256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java index 4a3a0bc4f8a..1ebe1389d9c 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Double512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Double512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Double512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Double512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java index 7d242995c1f..4fb167a19c4 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Double64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Double64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Double64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Double64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java index 137f7ba5791..78bec977fda 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ public class DoubleMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ public class DoubleMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ public class DoubleMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ public class DoubleMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Double.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Double.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java index 719b803b69b..c411dd1024b 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Float128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Float128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Float128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Float128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java index 26f2f02883f..6d339ad7a38 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Float256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Float256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Float256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Float256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java index 53e75fa62c7..427494c9725 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Float512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Float512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Float512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Float512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java index 6ea43adfec4..bf932f80355 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Float64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Float64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Float64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Float64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java index f67bf1e3267..550374dc454 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ public class FloatMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ public class FloatMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ public class FloatMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ public class FloatMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Float.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Float.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java index d6bf4dedc3e..04f1f7d6e66 100644 --- a/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Int128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Int128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Int128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Int128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java index c2d0077a8cd..a7b503a95f5 100644 --- a/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Int256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Int256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Int256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Int256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java index e9b70d9c93b..2843eaa9ea8 100644 --- a/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Int512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Int512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Int512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Int512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java index 28633c1b345..cd2c72ba977 100644 --- a/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Int64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Int64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Int64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Int64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java index f128184026b..7138431a948 100644 --- a/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ public class IntMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ public class IntMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ public class IntMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ public class IntMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "intByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Integer.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Integer.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java index 81b5ebd8fff..33dc4dcff24 100644 --- a/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Long128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Long128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Long128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Long128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java index ca6d4d74243..ce940c38dba 100644 --- a/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Long256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Long256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Long256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Long256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java index 5ad19715622..662b8e542a0 100644 --- a/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Long512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Long512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Long512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Long512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java index 1041d47d96c..74bfe78b1f8 100644 --- a/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Long64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Long64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Long64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Long64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java index e20d33d2026..580b7c34b4f 100644 --- a/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ public class LongMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ public class LongMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ public class LongMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ public class LongMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "longByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Long.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Long.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java index 2a153318cae..5c90c6c5f9d 100644 --- a/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Short128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Short128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Short128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Short128VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java index 407f5affdf4..6e0cb97469d 100644 --- a/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Short256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Short256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Short256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Short256VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java index d32d6735030..1635cab3538 100644 --- a/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Short512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Short512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Short512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Short512VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java index 6ab8817b22d..48dd41e599e 100644 --- a/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ public class Short64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ public class Short64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ public class Short64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ public class Short64VectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java index 7fb1c43ba3b..6f5b7e69dbb 100644 --- a/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ public class ShortMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ public class ShortMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ public class ShortMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ public class ShortMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest { @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, Short.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), Short.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template b/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template index ada362cee8f..a9d46118cbc 100644 --- a/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template +++ b/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template @@ -37,7 +37,7 @@ #warn This file is preprocessed before being compiled import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.$Type$Vector; import jdk.incubator.vector.VectorMask; @@ -499,8 +499,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction<$type$[]> fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, $Boxtype$.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), $Boxtype$.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -528,8 +528,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction<$type$[]> fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, $Boxtype$.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), $Boxtype$.SIZE); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -592,8 +592,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction<$type$[]> fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, $Boxtype$.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), $Boxtype$.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Boxtype$> vmask = VectorMask.fromValues(SPECIES, mask); @@ -623,8 +623,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction<$type$[]> fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> Arena.ofAuto().allocate(i, $Boxtype$.SIZE)); + MemorySegment r = Arena.ofAuto().allocate(a.byteSize(), $Boxtype$.SIZE); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Boxtype$> vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java b/test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java deleted file mode 100644 index 61b705e5b26..00000000000 --- a/test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2021, 2022, 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 org.openjdk.bench.java.lang.foreign; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Warmup; - -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -@BenchmarkMode(Mode.AverageTime) -@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) -@State(org.openjdk.jmh.annotations.Scope.Thread) -@OutputTimeUnit(TimeUnit.MILLISECONDS) -@Fork(value = 3, jvmArgsAppend = "--enable-preview") -public class BulkMismatchAcquire { - - public enum SessionKind { - CONFINED(Arena::openConfined), - SHARED(Arena::openShared); - - final Supplier arenaFactory; - - SessionKind(Supplier arenaFactory) { - this.arenaFactory = arenaFactory; - } - - Arena makeArena() { - return arenaFactory.get(); - } - } - - @Param({"CONFINED", "SHARED"}) - public BulkMismatchAcquire.SessionKind sessionKind; - - // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized - static final int SIZE_WITH_TAIL = (1024 * 1024) + 7; - - Arena arena; - MemorySegment mismatchSegmentLarge1; - MemorySegment mismatchSegmentLarge2; - ByteBuffer mismatchBufferLarge1; - ByteBuffer mismatchBufferLarge2; - MemorySegment mismatchSegmentSmall1; - MemorySegment mismatchSegmentSmall2; - ByteBuffer mismatchBufferSmall1; - ByteBuffer mismatchBufferSmall2; - - @Setup - public void setup() { - arena = sessionKind.makeArena(); - mismatchSegmentLarge1 = arena.allocate(SIZE_WITH_TAIL); - mismatchSegmentLarge2 = arena.allocate(SIZE_WITH_TAIL); - mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); - mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); - - // mismatch at first byte - mismatchSegmentSmall1 = arena.allocate(7); - mismatchSegmentSmall2 = arena.allocate(7); - mismatchBufferSmall1 = ByteBuffer.allocateDirect(7); - mismatchBufferSmall2 = ByteBuffer.allocateDirect(7); - { - mismatchSegmentSmall1.fill((byte) 0xFF); - mismatchBufferSmall1.put((byte) 0xFF).clear(); - // verify expected mismatch indices - long si = mismatchSegmentLarge1.mismatch(mismatchSegmentLarge2); - if (si != -1) - throw new AssertionError("Unexpected mismatch index:" + si); - int bi = mismatchBufferLarge1.mismatch(mismatchBufferLarge2); - if (bi != -1) - throw new AssertionError("Unexpected mismatch index:" + bi); - si = mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2); - if (si != 0) - throw new AssertionError("Unexpected mismatch index:" + si); - bi = mismatchBufferSmall1.mismatch(mismatchBufferSmall2); - if (bi != 0) - throw new AssertionError("Unexpected mismatch index:" + bi); - } - } - - @TearDown - public void tearDown() { - arena.close(); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public long mismatch_large_segment() { - return mismatchSegmentLarge1.mismatch(mismatchSegmentLarge2); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public long mismatch_large_segment_acquire() { - long[] arr = new long[1]; - mismatchSegmentLarge1.scope().whileAlive(() -> { - arr[0] = mismatchSegmentLarge1.mismatch(mismatchSegmentSmall2); - }); - return arr[0]; - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public int mismatch_large_bytebuffer() { - return mismatchBufferLarge1.mismatch(mismatchBufferLarge2); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public long mismatch_small_segment() { - return mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public long mismatch_small_segment_acquire() { - long[] arr = new long[1]; - mismatchSegmentLarge1.scope().whileAlive(() -> { - arr[0] = mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2); - }); - return arr[0]; - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public int mismatch_small_bytebuffer() { - return mismatchBufferSmall1.mismatch(mismatchBufferSmall2); - } -} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java b/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java index 64ad5c4b377..a521192e924 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java @@ -43,7 +43,6 @@ import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.lang.foreign.ValueLayout.JAVA_INT; import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED; @@ -61,10 +60,11 @@ public class BulkOps { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; - final Arena arena = Arena.openShared(); + final Arena arena = Arena.ofShared(); final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE); - final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); + final MemorySegment segment = arena.allocate(ALLOC_SIZE, 1); + final IntBuffer buffer = IntBuffer.allocate(ELEM_SIZE); final int[] ints = new int[ELEM_SIZE]; @@ -73,14 +73,19 @@ public class BulkOps { // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized static final int SIZE_WITH_TAIL = (1024 * 1024) + 7; - final MemorySegment mismatchSegmentLarge1 = MemorySegment.allocateNative(SIZE_WITH_TAIL, arena.scope()); - final MemorySegment mismatchSegmentLarge2 = MemorySegment.allocateNative(SIZE_WITH_TAIL, arena.scope());; + final MemorySegment mismatchSegmentLarge1; + + { + mismatchSegmentLarge1 = arena.allocate(SIZE_WITH_TAIL, 1); + } + + final MemorySegment mismatchSegmentLarge2 = arena.allocate(SIZE_WITH_TAIL, 1); final ByteBuffer mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); final ByteBuffer mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); // mismatch at first byte - final MemorySegment mismatchSegmentSmall1 = MemorySegment.allocateNative(7, arena.scope());; - final MemorySegment mismatchSegmentSmall2 = MemorySegment.allocateNative(7, arena.scope());; + final MemorySegment mismatchSegmentSmall1 = arena.allocate(7, 1); + final MemorySegment mismatchSegmentSmall2 = arena.allocate(7, 1); final ByteBuffer mismatchBufferSmall1 = ByteBuffer.allocateDirect(7); final ByteBuffer mismatchBufferSmall2 = ByteBuffer.allocateDirect(7); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java b/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java index d88838905fd..7085f7af530 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java @@ -23,8 +23,10 @@ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.AddressLayout; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; @@ -66,15 +68,16 @@ public class CLayouts { /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.asUnbounded(); + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(C_CHAR)); private static Linker LINKER = Linker.nativeLinker(); private static final MethodHandle FREE = LINKER.downcallHandle( - LINKER.defaultLookup().find("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); + LINKER.defaultLookup().find("free").get(), FunctionDescriptor.ofVoid(C_POINTER)); private static final MethodHandle MALLOC = LINKER.downcallHandle( - LINKER.defaultLookup().find("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS.asUnbounded(), ValueLayout.JAVA_LONG)); + LINKER.defaultLookup().find("malloc").get(), FunctionDescriptor.of(C_POINTER, ValueLayout.JAVA_LONG)); public static void freeMemory(MemorySegment address) { try { diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java index e542276ccf6..3e1614f32dc 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java @@ -54,6 +54,11 @@ public class CallOverheadConstant { func.invokeExact(); } + @Benchmark + public void panama_blank_trivial() throws Throwable { + func_trivial.invokeExact(); + } + @Benchmark public int jni_identity() throws Throwable { return identity(10); @@ -64,6 +69,11 @@ public class CallOverheadConstant { return (int) identity.invokeExact(10); } + @Benchmark + public int panama_identity_trivial() throws Throwable { + return (int) identity_trivial.invokeExact(10); + } + @Benchmark public MemorySegment panama_identity_struct_confined() throws Throwable { return (MemorySegment) identity_struct.invokeExact(recycling_allocator, confinedPoint); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java index bbbc0afbb0f..26588b6bd08 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java @@ -22,15 +22,8 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.*; -import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -41,10 +34,14 @@ public class CallOverheadHelper extends CLayouts { static final Linker abi = Linker.nativeLinker(); static final MethodHandle func; + static final MethodHandle func_trivial; static final MethodHandle func_v; + static final MethodHandle func_trivial_v; static MemorySegment func_addr; static final MethodHandle identity; + static final MethodHandle identity_trivial; static final MethodHandle identity_v; + static final MethodHandle identity_trivial_v; static MemorySegment identity_addr; static final MethodHandle identity_struct; static final MethodHandle identity_struct_v; @@ -81,14 +78,32 @@ public class CallOverheadHelper extends CLayouts { C_INT, C_INT ); - static final MemorySegment sharedPoint = MemorySegment.allocateNative(POINT_LAYOUT, Arena.openShared().scope()); - static final MemorySegment confinedPoint = MemorySegment.allocateNative(POINT_LAYOUT, Arena.openConfined().scope()); - - static final MemorySegment point = MemorySegment.allocateNative(POINT_LAYOUT, SegmentScope.auto()); - - static final SegmentAllocator recycling_allocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(POINT_LAYOUT, SegmentScope.auto())); + static final MemorySegment sharedPoint; static { + Arena scope = Arena.ofShared(); + sharedPoint = scope.allocate(POINT_LAYOUT); + } + + static final MemorySegment confinedPoint; + + static { + Arena scope = Arena.ofConfined(); + confinedPoint = scope.allocate(POINT_LAYOUT); + } + + static final MemorySegment point; + + static { + Arena scope = Arena.ofAuto(); + point = scope.allocate(POINT_LAYOUT); + } + + static final SegmentAllocator recycling_allocator; + + static { + Arena scope = Arena.ofAuto(); + recycling_allocator = SegmentAllocator.prefixAllocator(scope.allocate(POINT_LAYOUT)); System.loadLibrary("CallOverheadJNI"); System.loadLibrary("CallOverhead"); @@ -98,13 +113,17 @@ public class CallOverheadHelper extends CLayouts { MethodType mt = MethodType.methodType(void.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); func_v = abi.downcallHandle(fd); + func_trivial_v = abi.downcallHandle(fd, Linker.Option.isTrivial()); func = insertArguments(func_v, 0, func_addr); + func_trivial = insertArguments(func_trivial_v, 0, func_addr); } { identity_addr = loaderLibs.find("identity").orElseThrow(); FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT); identity_v = abi.downcallHandle(fd); + identity_trivial_v = abi.downcallHandle(fd, Linker.Option.isTrivial()); identity = insertArguments(identity_v, 0, identity_addr); + identity_trivial = insertArguments(identity_trivial_v, 0, identity_addr); } identity_struct_addr = loaderLibs.find("identity_struct").orElseThrow(); identity_struct_v = abi.downcallHandle( diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java index de3b275e034..6797eda9924 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java @@ -54,6 +54,11 @@ public class CallOverheadVirtual { func_v.invokeExact(func_addr); } + @Benchmark + public void panama_blank_trivial() throws Throwable { + func_trivial_v.invokeExact(func_addr); + } + @Benchmark public int jni_identity() throws Throwable { return identity(10); @@ -103,6 +108,11 @@ public class CallOverheadVirtual { return (int) identity_v.invokeExact(identity_addr, 10); } + @Benchmark + public int panama_identity_trivial() throws Throwable { + return (int) identity_trivial_v.invokeExact(identity_addr, 10); + } + @Benchmark public MemorySegment panama_identity_struct() throws Throwable { return (MemorySegment) identity_struct_v.invokeExact(identity_struct_addr, recycling_allocator, point); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java b/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java index 56ebaad5410..1c9e886e146 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java @@ -36,5 +36,4 @@ public class JavaLayouts { static final VarHandle VH_INT_UNALIGNED = JAVA_INT_UNALIGNED.arrayElementVarHandle(); static final VarHandle VH_INT = JAVA_INT.arrayElementVarHandle(); - } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java b/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java index 2b0b1935863..0adbc72e5b4 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java @@ -32,11 +32,10 @@ import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.Arena; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.concurrent.TimeUnit; @@ -65,8 +64,8 @@ public class LinkUpcall extends CLayouts { @Benchmark public MemorySegment link_blank() { - try (Arena arena = Arena.openConfined()) { - return LINKER.upcallStub(BLANK, BLANK_DESC, arena.scope()); + try (Arena arena = Arena.ofConfined()) { + return LINKER.upcallStub(BLANK, BLANK_DESC, arena); } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java index a01891894f7..c592c8c9599 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java @@ -36,7 +36,7 @@ import org.openjdk.jmh.annotations.Warmup; import sun.misc.Unsafe; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; @@ -69,9 +69,11 @@ public class LoopOverConstant extends JavaLayouts { //setup native memory segment - static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, SegmentScope.auto()); + static final MemorySegment segment; static { + Arena scope = Arena.ofAuto(); + segment = scope.allocate(ALLOC_SIZE, 1); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java index 05be20222fa..73a7f803164 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java @@ -22,11 +22,7 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.*; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -60,8 +56,8 @@ public class LoopOverNew extends JavaLayouts { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; static final MemoryLayout ALLOC_LAYOUT = MemoryLayout.sequenceLayout(ELEM_SIZE, JAVA_INT); - final Arena arena = Arena.openConfined(); - final SegmentAllocator recyclingAlloc = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(ALLOC_LAYOUT, arena.scope())); + final Arena arena = Arena.ofConfined(); + final SegmentAllocator recyclingAlloc = SegmentAllocator.prefixAllocator(arena.allocate(ALLOC_LAYOUT)); @TearDown public void tearDown() throws Throwable { @@ -79,7 +75,7 @@ public class LoopOverNew extends JavaLayouts { @Benchmark public void segment_loop_confined() { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(ALLOC_SIZE, 4); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); @@ -89,7 +85,7 @@ public class LoopOverNew extends JavaLayouts { @Benchmark public void segment_loop_shared() { - try (Arena arena = Arena.openShared()) { + try (Arena arena = Arena.ofShared()) { MemorySegment segment = arena.allocate(ALLOC_SIZE, 4); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); @@ -135,7 +131,8 @@ public class LoopOverNew extends JavaLayouts { @Benchmark public void segment_loop_implicit() { if (gcCount++ == 0) System.gc(); // GC when we overflow - MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, 4, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment segment = scope.allocate(ALLOC_SIZE, 4); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java index c5b697b8ac8..46c4091a8bf 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java @@ -69,8 +69,8 @@ public class LoopOverNonConstant extends JavaLayouts { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i); } - arena = Arena.openConfined(); - segment = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); + arena = Arena.ofConfined(); + segment = arena.allocate(ALLOC_SIZE, 1); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java index 430a3b69d04..2bfb3ef2a03 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java @@ -72,9 +72,9 @@ public class LoopOverNonConstantFP { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putDouble(unsafe_addrOut + (i * CARRIER_SIZE), i); } - arena = Arena.openConfined(); - segmentIn = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); - segmentOut = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); + arena = Arena.ofConfined(); + segmentIn = arena.allocate(ALLOC_SIZE, 1); + segmentOut = arena.allocate(ALLOC_SIZE, 1); for (int i = 0; i < ELEM_SIZE; i++) { segmentIn.setAtIndex(JAVA_DOUBLE, i, i); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java index 386fe2691ea..3b7b8052f5f 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java @@ -22,6 +22,7 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; @@ -37,7 +38,6 @@ import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; import sun.misc.Unsafe; -import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; @@ -76,7 +76,8 @@ public class LoopOverNonConstantHeap extends JavaLayouts { MemorySegment intI = MemorySegment.ofArray(new int[ALLOC_SIZE]); MemorySegment intD = MemorySegment.ofArray(new double[ALLOC_SIZE]); MemorySegment intF = MemorySegment.ofArray(new float[ALLOC_SIZE]); - MemorySegment s = MemorySegment.allocateNative(ALLOC_SIZE, 1, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + MemorySegment s = scope.allocate(ALLOC_SIZE, 1); for (int i = 0; i < ALLOC_SIZE; i++) { intB.set(JAVA_BYTE, i, (byte)i); intI.setAtIndex(JAVA_INT, i, i); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java index 0572223f84e..43ea54f6703 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java @@ -95,8 +95,8 @@ public class LoopOverNonConstantMapped extends JavaLayouts { ((MappedByteBuffer)byteBuffer).force(); } fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE); - arena = Arena.openConfined(); - segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, ALLOC_SIZE, arena.scope()); + arena = Arena.ofConfined(); + segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, ALLOC_SIZE, arena); unsafe_addr = segment.address(); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java index f3d47fed5d1..f3c440e5879 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java @@ -68,8 +68,8 @@ public class LoopOverNonConstantShared extends JavaLayouts { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i); } - arena = Arena.openConfined(); - segment = MemorySegment.allocateNative(ALLOC_SIZE, CARRIER_SIZE, arena.scope()); + arena = Arena.ofConfined(); + segment = arena.allocate(ALLOC_SIZE, CARRIER_SIZE); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverOfAddress.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverOfAddress.java index 2463856fbdf..fd6ba441589 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverOfAddress.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverOfAddress.java @@ -22,6 +22,7 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; @@ -35,7 +36,6 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; -import java.lang.foreign.SegmentScope; import java.util.concurrent.TimeUnit; @BenchmarkMode(Mode.AverageTime) @@ -61,7 +61,8 @@ public class LoopOverOfAddress extends JavaLayouts { public long segment_loop_addr_size() { long res = 0; for (int i = 0; i < ITERATIONS; i++) { - res += MemorySegment.ofAddress(i, i % 100).address(); + res += MemorySegment.ofAddress(i) + .reinterpret(i % 100).address(); } return res; } @@ -70,7 +71,8 @@ public class LoopOverOfAddress extends JavaLayouts { public long segment_loop_addr_size_session() { long res = 0; for (int i = 0; i < ITERATIONS; i++) { - res += MemorySegment.ofAddress(i, i % 100, SegmentScope.global()).address(); + res += MemorySegment.ofAddress(i) + .reinterpret(i % 100, Arena.global(), null).address(); } return res; } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java index 425a1adce44..d396432d6e2 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java @@ -68,10 +68,12 @@ public class LoopOverPollutedSegments extends JavaLayouts { unsafe.putInt(addr + (i * 4), i); } arr = new byte[ALLOC_SIZE]; - confinedArena = Arena.openConfined(); - sharedArena = Arena.openShared(); - nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, confinedArena.scope()); - nativeSharedSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, sharedArena.scope()); + confinedArena = Arena.ofConfined(); + sharedArena = Arena.ofShared(); + Arena scope1 = confinedArena; + nativeSegment = scope1.allocate(ALLOC_SIZE, 4); + Arena scope = sharedArena; + nativeSharedSegment = scope.allocate(ALLOC_SIZE, 4); heapSegmentBytes = MemorySegment.ofArray(new byte[ALLOC_SIZE]); heapSegmentFloats = MemorySegment.ofArray(new float[ELEM_SIZE]); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java index 23ac72d0e68..2979547ba40 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java @@ -63,8 +63,8 @@ public class LoopOverSlice { @Setup public void setup() { - arena = Arena.openConfined(); - nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); + arena = Arena.ofConfined(); + nativeSegment = arena.allocate(ALLOC_SIZE, 1); heapSegment = MemorySegment.ofArray(new int[ELEM_SIZE]); nativeBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer(); heapBuffer = IntBuffer.wrap(new int[ELEM_SIZE]); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/MemorySegmentVsBits.java b/test/micro/org/openjdk/bench/java/lang/foreign/MemorySegmentVsBits.java new file mode 100644 index 00000000000..5ad20afeebf --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/MemorySegmentVsBits.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022, 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 org.openjdk.bench.java.lang.foreign; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteBuffer; +import java.nio.LongBuffer; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static java.lang.foreign.ValueLayout.*; +import static java.nio.ByteOrder.BIG_ENDIAN; + +/** + * This benchmark creates an array of longs with random contents. The array + * is then copied into a byte array (using big endian) using different + * methods. + */ +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3, jvmArgsAppend = {"--enable-native-access=ALL-UNNAMED", "--enable-preview"}) +public class MemorySegmentVsBits { + + public static final VarHandle LONG_ARRAY_VH = MethodHandles.byteArrayViewVarHandle(long[].class, BIG_ENDIAN); + + Arena arena = Arena.ofConfined(); + + @Param({"1", "2", "16", "64", "256"}) + public int size; + private long[] longs; + private byte[] bytes; + + private ByteBuffer byteBuffer; + private LongBuffer longBuffer; + private MemorySegment segment; + private MemorySegment nativeSegment; + + private static final ValueLayout.OfLong OF_LONG = (JAVA_LONG.order() != BIG_ENDIAN) + ? JAVA_LONG.withOrder(BIG_ENDIAN) + : JAVA_LONG; + + @Setup + public void setup() { + longs = ThreadLocalRandom.current().longs(size).toArray(); + bytes = new byte[size * Long.BYTES]; + byteBuffer = ByteBuffer.wrap(bytes); + longBuffer = byteBuffer.asLongBuffer(); + segment = MemorySegment.ofArray(bytes); + nativeSegment = arena.allocate(size * Long.BYTES); + } + + @TearDown + public void tearDown() { + arena.close(); + } + + @Benchmark + public void bitsEquivalent() { + for (int i = 0; i < size; i++) { + putLong(bytes, i * Long.BYTES, longs[i]); + } + } + @Benchmark + public void byteVarHandle() { + for (int i = 0; i < size; i++) { + LONG_ARRAY_VH.set(bytes, i * Long.BYTES, longs[i]); + } + } + @Benchmark + public void byteBuffer() { + for (int i = 0; i < size; i++) { + byteBuffer.putLong(i * Long.BYTES, longs[i]); + } + } + + @Benchmark + public void longBuffer() { + for (int i = 0; i < size; i++) { + longBuffer.put(i, longs[i]); + } + } + + @Benchmark + public void panamaHeap() { + for (int i = 0; i < size; i++) { + segment.set(JAVA_LONG_UNALIGNED, i * Long.BYTES, longs[i]); + } + } + + @Benchmark + public void panamaNative() { + for (int i = 0; i < size; i++) { + nativeSegment.set(OF_LONG, i * Long.BYTES, longs[i]); + } + } + + @Benchmark + public void panamaNativeUnaligned() { + for (int i = 0; i < size; i++) { + nativeSegment.set(JAVA_LONG_UNALIGNED, i * Long.BYTES, longs[i]); + } + } + + // java.io.Bits is package private + static void putLong(byte[] b, int off, long val) { + b[off + 7] = (byte) (val); + b[off + 6] = (byte) (val >>> 8); + b[off + 5] = (byte) (val >>> 16); + b[off + 4] = (byte) (val >>> 24); + b[off + 3] = (byte) (val >>> 32); + b[off + 2] = (byte) (val >>> 40); + b[off + 1] = (byte) (val >>> 48); + b[off] = (byte) (val >>> 56); + } + +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java b/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java index 6df2fb0d6c1..fa4207709ed 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java @@ -22,7 +22,6 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; @@ -37,7 +36,7 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -104,27 +103,27 @@ public class MemorySessionClose { @Benchmark public MemorySegment confined_close() { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { return arena.allocate(ALLOC_SIZE, 4); } } @Benchmark public MemorySegment shared_close() { - try (Arena arena = Arena.openShared()) { + try (Arena arena = Arena.ofShared()) { return arena.allocate(ALLOC_SIZE, 4); } } @Benchmark public MemorySegment implicit_close() { - return MemorySegment.allocateNative(ALLOC_SIZE, 4, SegmentScope.auto()); + return Arena.ofAuto().allocate(ALLOC_SIZE, 4); } @Benchmark public MemorySegment implicit_close_systemgc() { if (gcCount++ == 0) System.gc(); // GC when we overflow - return MemorySegment.allocateNative(ALLOC_SIZE, 4, SegmentScope.auto()); + return Arena.ofAuto().allocate(ALLOC_SIZE, 4); } // keep diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java b/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java index 78575b3c45c..1579951127e 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java @@ -23,10 +23,7 @@ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Arena; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.SequenceLayout; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; import sun.misc.Unsafe; import org.openjdk.jmh.annotations.Benchmark; @@ -40,7 +37,6 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; -import java.lang.foreign.MemorySegment; import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -79,8 +75,8 @@ public class ParallelSum extends JavaLayouts { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(address + (i * CARRIER_SIZE), i); } - arena = Arena.openShared(); - segment = MemorySegment.allocateNative(ALLOC_SIZE, CARRIER_SIZE, arena.scope()); + arena = Arena.ofShared(); + segment = arena.allocate(ALLOC_SIZE, CARRIER_SIZE); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java b/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java index 6f4aa4c17b6..ccf0c8a4c71 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java @@ -25,10 +25,7 @@ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Arena; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; +import java.lang.foreign.*; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -40,7 +37,6 @@ import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.util.concurrent.TimeUnit; @@ -52,22 +48,26 @@ import java.util.concurrent.TimeUnit; @Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED", "--enable-preview" }) public class PointerInvoke extends CLayouts { - Arena arena = Arena.openConfined(); - MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); + Arena arena = Arena.ofConfined(); + MemorySegment segment = arena.allocate(100, 1); static { System.loadLibrary("Ptr"); } - static final MethodHandle F_LONG, F_PTR; + static final MethodHandle F_LONG_LONG, F_PTR_LONG, F_LONG_PTR, F_PTR_PTR; static { Linker abi = Linker.nativeLinker(); SymbolLookup loaderLibs = SymbolLookup.loaderLookup(); - F_LONG = abi.downcallHandle(loaderLibs.find("func_as_long").get(), - FunctionDescriptor.of(C_INT, C_LONG_LONG)); - F_PTR = abi.downcallHandle(loaderLibs.find("func_as_ptr").get(), - FunctionDescriptor.of(C_INT, C_POINTER)); + F_LONG_LONG = abi.downcallHandle(loaderLibs.find("id_long_long").get(), + FunctionDescriptor.of(C_LONG_LONG, C_LONG_LONG)); + F_PTR_LONG = abi.downcallHandle(loaderLibs.find("id_ptr_long").get(), + FunctionDescriptor.of(C_LONG_LONG, C_POINTER)); + F_LONG_PTR = abi.downcallHandle(loaderLibs.find("id_long_ptr").get(), + FunctionDescriptor.of(C_POINTER, C_LONG_LONG)); + F_PTR_PTR = abi.downcallHandle(loaderLibs.find("id_ptr_ptr").get(), + FunctionDescriptor.of(C_POINTER, C_POINTER)); } @TearDown @@ -76,18 +76,34 @@ public class PointerInvoke extends CLayouts { } @Benchmark - public int panama_call_as_long() throws Throwable { - return (int)F_LONG.invokeExact(segment.address()); + public long long_to_long() throws Throwable { + return (long)F_LONG_LONG.invokeExact(segment.address()); } @Benchmark - public int panama_call_as_address() throws Throwable { - return (int)F_PTR.invokeExact(segment); + public long ptr_to_long() throws Throwable { + return (long)F_PTR_LONG.invokeExact(segment); } @Benchmark - public int panama_call_as_new_segment() throws Throwable { - MemorySegment newSegment = MemorySegment.ofAddress(segment.address(), 100, arena.scope()); - return (int)F_PTR.invokeExact(newSegment); + public long ptr_to_long_new_segment() throws Throwable { + MemorySegment newSegment = segment.reinterpret(100, arena, null); + return (long)F_PTR_LONG.invokeExact(newSegment); + } + + @Benchmark + public long long_to_ptr() throws Throwable { + return ((MemorySegment)F_LONG_PTR.invokeExact(segment.address())).address(); + } + + @Benchmark + public long ptr_to_ptr() throws Throwable { + return ((MemorySegment)F_PTR_PTR.invokeExact(segment)).address(); + } + + @Benchmark + public long ptr_to_ptr_new_segment() throws Throwable { + MemorySegment newSegment = segment.reinterpret(100, arena, null); + return ((MemorySegment)F_PTR_PTR.invokeExact(newSegment)).address(); } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java b/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java index 0b8b4e9708e..90b7693db3d 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java @@ -22,10 +22,7 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; +import java.lang.foreign.*; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -36,8 +33,7 @@ import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SymbolLookup; +import java.lang.foreign.Arena; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.concurrent.TimeUnit; @@ -65,7 +61,8 @@ public class QSort extends CLayouts { static MemorySegment qsort_addr = abi.defaultLookup().find("qsort").get(); static { - INPUT_SEGMENT = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(INPUT.length, JAVA_INT), SegmentScope.global()); + MemoryLayout layout = MemoryLayout.sequenceLayout(INPUT.length, JAVA_INT); + INPUT_SEGMENT = Arena.global().allocate(layout); INPUT_SEGMENT.copyFrom(MemorySegment.ofArray(INPUT)); System.loadLibrary("QSortJNI"); @@ -83,7 +80,7 @@ public class QSort extends CLayouts { "panama_upcall_compar", MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class)), FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER), - SegmentScope.global() + Arena.global() ); } catch (ReflectiveOperationException e) { throw new BootstrapMethodError(e); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java index cd943a022f4..496a3bf397e 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java @@ -25,12 +25,7 @@ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Arena; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.*; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -44,6 +39,7 @@ import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.MemorySegment.Scope; import java.lang.invoke.MethodHandle; import java.util.concurrent.TimeUnit; @@ -57,10 +53,11 @@ import static java.lang.foreign.ValueLayout.JAVA_BYTE; @Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED", "--enable-preview" }) public class StrLenTest extends CLayouts { - Arena arena = Arena.openConfined(); + Arena arena = Arena.ofConfined(); SegmentAllocator segmentAllocator; - SegmentAllocator arenaAllocator = new RingAllocator(arena.scope()); + SegmentAllocator arenaAllocator = new RingAllocator(arena); + SlicingPool pool = new SlicingPool(); @Param({"5", "20", "100"}) public int size; @@ -81,7 +78,7 @@ public class StrLenTest extends CLayouts { @Setup public void setup() { str = makeString(size); - segmentAllocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(size + 1, arena.scope())); + segmentAllocator = SegmentAllocator.prefixAllocator(arena.allocate(size + 1, 1)); } @TearDown @@ -96,7 +93,7 @@ public class StrLenTest extends CLayouts { @Benchmark public int panama_strlen() throws Throwable { - try (Arena arena = Arena.openConfined()) { + try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocateUtf8String(str); return (int)STRLEN.invokeExact(segment); } @@ -107,6 +104,14 @@ public class StrLenTest extends CLayouts { return (int)STRLEN.invokeExact(arenaAllocator.allocateUtf8String(str)); } + @Benchmark + public int panama_strlen_pool() throws Throwable { + Arena arena = pool.acquire(); + int l = (int) STRLEN.invokeExact(arena.allocateUtf8String(str)); + arena.close(); + return l; + } + @Benchmark public int panama_strlen_prefix() throws Throwable { return (int)STRLEN.invokeExact(segmentAllocator.allocateUtf8String(str)); @@ -148,8 +153,8 @@ public class StrLenTest extends CLayouts { SegmentAllocator current; long rem; - public RingAllocator(SegmentScope session) { - this.segment = MemorySegment.allocateNative(1024, session); + public RingAllocator(Arena session) { + this.segment = session.allocate(1024, 1); reset(); } @@ -169,4 +174,38 @@ public class StrLenTest extends CLayouts { rem = segment.byteSize(); } } + + static class SlicingPool { + final MemorySegment pool = Arena.ofAuto().allocate(1024); + boolean isAcquired = false; + + public Arena acquire() { + if (isAcquired) { + throw new IllegalStateException("An allocator is already in use"); + } + isAcquired = true; + return new SlicingPoolAllocator(); + } + + class SlicingPoolAllocator implements Arena { + + final Arena arena = Arena.ofConfined(); + final SegmentAllocator slicing = SegmentAllocator.slicingAllocator(pool); + + public MemorySegment allocate(long byteSize, long byteAlignment) { + return slicing.allocate(byteSize, byteAlignment) + .reinterpret(arena, null); + } + + @Override + public Scope scope() { + return arena.scope(); + } + + public void close() { + isAcquired = false; + arena.close(); + } + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java b/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java index c1ff656f780..40d09b7b762 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java @@ -37,7 +37,7 @@ import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; @@ -68,7 +68,8 @@ public class TestLoadBytes { } srcBufferNative = ByteBuffer.allocateDirect(size); - srcSegmentImplicit = MemorySegment.allocateNative(size, SegmentScope.auto()); + Arena scope = Arena.ofAuto(); + srcSegmentImplicit = scope.allocate(size, 1); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java b/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java index 5586aa866c6..2ba4ac2c414 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java @@ -24,7 +24,7 @@ package org.openjdk.bench.java.lang.foreign; import java.lang.foreign.*; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.lang.invoke.VarHandle; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; @@ -67,8 +67,10 @@ public class UnrolledAccess extends JavaLayouts { this.outputArray = new double[SIZE]; this.inputAddress = U.allocateMemory(8 * SIZE); this.outputAddress = U.allocateMemory(8 * SIZE); - this.inputSegment = MemorySegment.ofAddress(inputAddress, 8*SIZE, SegmentScope.global()); - this.outputSegment = MemorySegment.ofAddress(outputAddress, 8*SIZE, SegmentScope.global()); + this.inputSegment = MemorySegment.ofAddress(inputAddress) + .reinterpret(8*SIZE); + this.outputSegment = MemorySegment.ofAddress(outputAddress) + .reinterpret(8*SIZE); } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java b/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java index ed79a30b6e8..e0de2fbfcb7 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java @@ -22,9 +22,7 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.*; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -35,8 +33,7 @@ import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import java.lang.foreign.SegmentScope; -import java.lang.foreign.SymbolLookup; +import java.lang.foreign.Arena; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.concurrent.TimeUnit; @@ -131,7 +128,7 @@ public class Upcalls extends CLayouts { static MemorySegment makeCB(String name, MethodType mt, FunctionDescriptor fd) throws ReflectiveOperationException { return abi.upcallStub( lookup().findStatic(Upcalls.class, name, mt), - fd, SegmentScope.global() + fd, Arena.global() ); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/VaList.java b/test/micro/org/openjdk/bench/java/lang/foreign/VaList.java deleted file mode 100644 index a9205c22515..00000000000 --- a/test/micro/org/openjdk/bench/java/lang/foreign/VaList.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2020, 2022, 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. - * - * 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 org.openjdk.bench.java.lang.foreign; - -import java.lang.foreign.Arena; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -import java.lang.foreign.SymbolLookup; -import java.lang.invoke.MethodHandle; -import java.util.concurrent.TimeUnit; - -@BenchmarkMode(Mode.AverageTime) -@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) -@State(org.openjdk.jmh.annotations.Scope.Thread) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED", "--enable-preview" }) -public class VaList extends CLayouts { - - static final Linker linker = Linker.nativeLinker(); - static { - System.loadLibrary("VaList"); - } - - static final MethodHandle MH_ellipsis; - static final MethodHandle MH_vaList; - - static { - SymbolLookup loaderLibs = SymbolLookup.loaderLookup(); - MH_ellipsis = linker.downcallHandle(loaderLibs.find("ellipsis").get(), - FunctionDescriptor.ofVoid(C_INT, C_INT, C_DOUBLE, C_LONG_LONG), - Linker.Option.firstVariadicArg(1)); - MH_vaList = linker.downcallHandle(loaderLibs.find("vaList").get(), - FunctionDescriptor.ofVoid(C_INT, C_POINTER)); - } - - @Benchmark - public void ellipsis() throws Throwable { - MH_ellipsis.invokeExact(3, - 1, 2D, 3L); - } - - @Benchmark - public void vaList() throws Throwable { - try (Arena arena = Arena.openConfined()) { - java.lang.foreign.VaList vaList = java.lang.foreign.VaList.make(b -> - b.addVarg(C_INT, 1) - .addVarg(C_DOUBLE, 2D) - .addVarg(C_LONG_LONG, 3L), arena.scope()); - MH_vaList.invokeExact(3, - vaList.segment()); - } - } -} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java b/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java index e6501eae35f..4f8f9638c24 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java @@ -63,8 +63,8 @@ public class VarHandleExact { @Setup public void setup() { - arena = Arena.openConfined(); - data = MemorySegment.allocateNative(JAVA_INT, arena.scope()); + arena = Arena.ofConfined(); + data = arena.allocate(JAVA_INT); } @TearDown diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/libPtr.c b/test/micro/org/openjdk/bench/java/lang/foreign/libPtr.c index 5468379d59d..a26d679f18e 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/libPtr.c +++ b/test/micro/org/openjdk/bench/java/lang/foreign/libPtr.c @@ -29,10 +29,20 @@ #define EXPORT #endif -EXPORT int func_as_long(long long value) { - return 0; +#include + +EXPORT long long id_long_long(long long value) { + return value; } -EXPORT int func_as_ptr(void* ptr) { - return 0; +EXPORT long long id_ptr_long(void* ptr) { + return (long long)(size_t)ptr; +} + +EXPORT void* id_long_ptr(long long value) { + return (void*)(size_t)value; +} + +EXPORT void* id_ptr_ptr(void* ptr) { + return ptr; } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/NativeType.java b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/NativeType.java index 159464db390..9f1307f94a9 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/NativeType.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/NativeType.java @@ -23,6 +23,7 @@ package org.openjdk.bench.java.lang.foreign.pointers; +import java.lang.foreign.AddressLayout; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.ValueLayout; @@ -38,10 +39,11 @@ public sealed abstract class NativeType { public abstract ValueLayout.OfDouble layout(); } - private static final ValueLayout.OfAddress UNSAFE_ADDRESS = ValueLayout.ADDRESS.asUnbounded(); + private static final AddressLayout UNSAFE_ADDRESS = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE)); public final static class OfPointer extends NativeType { - public ValueLayout.OfAddress layout() { + public AddressLayout layout() { return UNSAFE_ADDRESS; } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/PointerBench.java b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/PointerBench.java index 1a1d196c2ae..cd13aa0290e 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/PointerBench.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/PointerBench.java @@ -35,7 +35,9 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.AddressLayout; import java.lang.foreign.Arena; +import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.util.concurrent.TimeUnit; @@ -48,7 +50,7 @@ import java.util.concurrent.TimeUnit; @State(Scope.Benchmark) public class PointerBench { - final Arena arena = Arena.openConfined(); + final Arena arena = Arena.ofConfined(); static final int ELEM_SIZE = 1_000_000; Pointer intPointer = Pointer.allocate(NativeType.C_INT, ELEM_SIZE, arena); Pointer> intPointerPointer = Pointer.allocate(NativeType.C_INT_PTR, ELEM_SIZE, arena); @@ -57,7 +59,8 @@ public class PointerBench { MemorySegment intPointerSegment = intPointerPointer.segment(); MemorySegment pointSegment = pointPointer.segment(); - public static final ValueLayout.OfAddress UNSAFE_ADDRESS = ValueLayout.ADDRESS.asUnbounded(); + public static final AddressLayout UNSAFE_ADDRESS = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE)); @Setup public void setup() { diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java b/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java index 1ed1d7a899c..27604fed6b8 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java @@ -22,15 +22,10 @@ */ package org.openjdk.bench.java.lang.foreign.points.support; -import java.lang.foreign.Arena; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; +import java.lang.foreign.*; import org.openjdk.bench.java.lang.foreign.CLayouts; -import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.lang.invoke.VarHandle; @@ -66,8 +61,8 @@ public class PanamaPoint extends CLayouts implements AutoCloseable { private final MemorySegment segment; public PanamaPoint(int x, int y) { - this.arena = Arena.openConfined(); - this.segment = MemorySegment.allocateNative(LAYOUT, arena.scope()); + this.arena = Arena.ofConfined(); + this.segment = arena.allocate(LAYOUT); setX(x); setY(y); } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java index 4f6c4aa65f2..65b2ccb5165 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java @@ -26,7 +26,7 @@ package org.openjdk.bench.jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; +import java.lang.foreign.Arena; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; import jdk.incubator.vector.ByteVector; @@ -69,8 +69,10 @@ public class MemorySegmentVectorAccess { @Setup public void setup() { - nativeIn = MemorySegment.allocateNative(size, SegmentScope.auto()); - nativeOut = MemorySegment.allocateNative(size, SegmentScope.auto()); + Arena scope1 = Arena.ofAuto(); + nativeIn = scope1.allocate(size, 1); + Arena scope = Arena.ofAuto(); + nativeOut = scope.allocate(size, 1); byteIn = new byte[size]; byteOut = new byte[size]; diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java index 62735fa3b00..7a42a247b12 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java @@ -25,7 +25,6 @@ package org.openjdk.bench.jdk.incubator.vector; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; import jdk.incubator.vector.ByteVector; @@ -85,8 +84,8 @@ public class TestLoadStoreBytes { srcSegmentHeap = MemorySegment.ofArray(new byte[size]); dstSegmentHeap = MemorySegment.ofArray(new byte[size]); - srcSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); - dstSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); + srcSegment = Arena.ofAuto().allocate(size, SPECIES.vectorByteSize()); + dstSegment = Arena.ofAuto().allocate(size, SPECIES.vectorByteSize()); a = new byte[size]; b = new byte[size]; @@ -163,9 +162,9 @@ public class TestLoadStoreBytes { @Benchmark public void segmentNativeConfined() { - try (final var arena = Arena.openConfined()) { - final var srcSegmentConfined = MemorySegment.ofAddress(srcSegment.address(), size, arena.scope()); - final var dstSegmentConfined = MemorySegment.ofAddress(dstSegment.address(), size, arena.scope()); + try (final var arena = Arena.ofConfined()) { + final var srcSegmentConfined = srcSegment.reinterpret(arena, null); + final var dstSegmentConfined = dstSegment.reinterpret(arena, null); for (long i = 0; i < SPECIES.loopBound(srcArray.length); i += SPECIES.length()) { var v = ByteVector.fromMemorySegment(SPECIES, srcSegmentConfined, i, ByteOrder.nativeOrder()); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java index ecbe5889806..2aec829fba8 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java @@ -24,7 +24,6 @@ package org.openjdk.bench.jdk.incubator.vector; import java.lang.foreign.Arena; -import java.lang.foreign.SegmentScope; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; @@ -90,8 +89,8 @@ public class TestLoadStoreShorts { srcSegmentHeap = MemorySegment.ofArray(new byte[size]); dstSegmentHeap = MemorySegment.ofArray(new byte[size]); - srcSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); - dstSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); + srcSegment = Arena.ofAuto().allocate(size, SPECIES.vectorByteSize()); + dstSegment = Arena.ofAuto().allocate(size, SPECIES.vectorByteSize()); this.longSize = longSize; @@ -161,9 +160,9 @@ public class TestLoadStoreShorts { @Benchmark public void segmentNativeConfined() { - try (final var arena = Arena.openConfined()) { - final var srcSegmentConfined = MemorySegment.ofAddress(srcSegment.address(), size, arena.scope()); - final var dstSegmentConfined = MemorySegment.ofAddress(dstSegment.address(), size, arena.scope()); + try (final var arena = Arena.ofConfined()) { + final var srcSegmentConfined = srcSegment.reinterpret(arena, null); + final var dstSegmentConfined = dstSegment.reinterpret(arena, null); for (long i = 0; i < SPECIES.loopBound(srcArray.length); i += SPECIES.length()) { var v = ShortVector.fromMemorySegment(SPECIES, srcSegmentConfined, i, ByteOrder.nativeOrder());