diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp
index a46a8be39d144..7a940a4b25a2e 100644
--- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp
@@ -210,23 +210,7 @@ void DowncallStubGenerator::generate() {
__ blr(_abi._target_addr_reg);
// this call is assumed not to have killed rthread
- if (!_needs_return_buffer) {
- // Unpack native results.
- switch (_ret_bt) {
- case T_BOOLEAN: __ c2bool(r0); break;
- case T_CHAR : __ ubfx(r0, r0, 0, 16); break;
- case T_BYTE : __ sbfx(r0, r0, 0, 8); break;
- case T_SHORT : __ sbfx(r0, r0, 0, 16); break;
- case T_INT : __ sbfx(r0, r0, 0, 32); break;
- case T_DOUBLE :
- case T_FLOAT :
- // Result is in v0 we'll save as needed
- break;
- case T_VOID: break;
- case T_LONG: break;
- default : ShouldNotReachHere();
- }
- } else {
+ if (_needs_return_buffer) {
assert(ret_buf_addr_sp_offset != -1, "no return buffer addr spill");
__ ldr(tmp1, Address(sp, ret_buf_addr_sp_offset));
int offset = 0;
diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp
index 06bf0099a917b..b7da306ad752b 100644
--- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp
+++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp
@@ -211,24 +211,7 @@ void DowncallStubGenerator::generate() {
__ call(_abi._target_addr_reg);
// this call is assumed not to have killed r15_thread
- if (!_needs_return_buffer) {
- // FIXME: this assumes we return in rax/xmm0, which might not be the case
- // Unpack native results.
- switch (_ret_bt) {
- case T_BOOLEAN: __ c2bool(rax); break;
- case T_CHAR : __ movzwl(rax, rax); break;
- case T_BYTE : __ sign_extend_byte (rax); break;
- case T_SHORT : __ sign_extend_short(rax); break;
- case T_INT : /* nothing to do */ break;
- case T_DOUBLE :
- case T_FLOAT :
- // Result is in xmm0 we'll save as needed
- break;
- case T_VOID: break;
- case T_LONG: break;
- default : ShouldNotReachHere();
- }
- } else {
+ if (_needs_return_buffer) {
assert(ret_buf_addr_rsp_offset != -1, "no return buffer addr spill");
__ movptr(rscratch1, Address(rsp, ret_buf_addr_rsp_offset));
int offset = 0;
diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp
index 485db1b2748a5..24bf59dcb91a5 100644
--- a/src/hotspot/share/ci/ciField.cpp
+++ b/src/hotspot/share/ci/ciField.cpp
@@ -225,7 +225,7 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
// Even if general trusting is disabled, trust system-built closures in these packages.
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") ||
holder->is_in_package("java/lang/reflect") || holder->is_in_package("jdk/internal/reflect") ||
- holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("java/lang/foreign") ||
+ holder->is_in_package("jdk/internal/foreign/layout") || holder->is_in_package("jdk/internal/foreign") ||
holder->is_in_package("jdk/internal/vm/vector") || holder->is_in_package("jdk/incubator/vector") ||
holder->is_in_package("java/lang"))
return true;
diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java
index 2f315b42cfcde..9ca560cfcba29 100644
--- a/src/java.base/share/classes/java/lang/Module.java
+++ b/src/java.base/share/classes/java/lang/Module.java
@@ -52,10 +52,12 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import jdk.internal.javac.PreviewFeature;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.ClassLoaders;
import jdk.internal.misc.CDS;
+import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleLoaderMap;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.module.Resources;
@@ -256,25 +258,77 @@ public ModuleLayer getLayer() {
/**
* Update this module to allow access to restricted methods.
*/
- Module implAddEnableNativeAccess() {
+ synchronized Module implAddEnableNativeAccess() {
enableNativeAccess = true;
return this;
}
/**
- * Update all unnamed modules to allow access to restricted methods.
+ * Returns {@code true} if this module can access
+ * restricted methods.
+ *
+ * @since 20
+ *
+ * @return {@code true} if this module can access restricted methods.
*/
- static void implAddEnableNativeAccessAllUnnamed() {
- ALL_UNNAMED_MODULE.enableNativeAccess = true;
+ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
+ public boolean isNativeAccessEnabled() {
+ Module target = moduleForNativeAccess();
+ synchronized(target) {
+ return target.enableNativeAccess;
+ }
}
+ // Returns the Module object that holds the enableNativeAccess
+ // flag for this module.
+ private Module moduleForNativeAccess() {
+ return isNamed() ? this : ALL_UNNAMED_MODULE;
+ }
+
+ // This is invoked from Reflection.ensureNativeAccess
+ void ensureNativeAccess(Class> owner, String methodName) {
+ // The target module whose enableNativeAccess flag is ensured
+ Module target = moduleForNativeAccess();
+ // racy read of the enable native access flag
+ boolean isNativeAccessEnabled = target.enableNativeAccess;
+ if (!isNativeAccessEnabled) {
+ synchronized (target) {
+ // safe read of the enableNativeAccess of the target module
+ isNativeAccessEnabled = target.enableNativeAccess;
+
+ // check again with the safely read flag
+ if (isNativeAccessEnabled) {
+ // another thread beat us to it - nothing to do
+ return;
+ } else if (ModuleBootstrap.hasEnableNativeAccessFlag()) {
+ throw new IllegalCallerException("Illegal native access from: " + this);
+ } else {
+ // warn and set flag, so that only one warning is reported per module
+ String cls = owner.getName();
+ String mtd = cls + "::" + methodName;
+ String mod = isNamed() ? "module " + getName() : "the unnamed module";
+ String modflag = isNamed() ? getName() : "ALL-UNNAMED";
+ System.err.printf("""
+ WARNING: A restricted method in %s has been called
+ WARNING: %s has been called by %s
+ WARNING: Use --enable-native-access=%s to avoid a warning for this module
+ %n""", cls, mtd, mod, modflag);
+
+ // set the flag
+ target.enableNativeAccess = true;
+ }
+ }
+ }
+ }
+
+
/**
- * Returns true if module m can access restricted methods.
+ * Update all unnamed modules to allow access to restricted methods.
*/
- boolean implIsEnableNativeAccess() {
- return isNamed() ?
- enableNativeAccess :
- ALL_UNNAMED_MODULE.enableNativeAccess;
+ static void implAddEnableNativeAccessToAllUnnamed() {
+ synchronized (ALL_UNNAMED_MODULE) {
+ ALL_UNNAMED_MODULE.enableNativeAccess = true;
+ }
}
// --
diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java
index 91e4dd873beed..444f8cdeb82f4 100644
--- a/src/java.base/share/classes/java/lang/ModuleLayer.java
+++ b/src/java.base/share/classes/java/lang/ModuleLayer.java
@@ -44,15 +44,17 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import jdk.internal.javac.PreviewFeature;
import jdk.internal.loader.ClassLoaderValue;
import jdk.internal.loader.Loader;
import jdk.internal.loader.LoaderPool;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.misc.CDS;
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.Stable;
import sun.security.util.SecurityConstants;
-
/**
* A layer of modules in the Java virtual machine.
*
@@ -297,6 +299,39 @@ public Controller addOpens(Module source, String pn, Module target) {
source.implAddOpens(pn, target);
return this;
}
+
+ /**
+ * Enables native access for a module in the layer if the caller's module
+ * has native access.
+ *
+ *
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 target
+ * The module to update
+ *
+ * @return This controller
+ *
+ * @throws IllegalArgumentException
+ * If {@code target} is not in the module layer
+ *
+ * @throws IllegalCallerException
+ * If the caller is in a module that does not have native access enabled
+ *
+ * @since 20
+ */
+ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
+ @CallerSensitive
+ public Controller enableNativeAccess(Module target) {
+ ensureInLayer(target);
+ Reflection.ensureNativeAccess(Reflection.getCallerClass(), Module.class,
+ "enableNativeAccess");
+ target.implAddEnableNativeAccess();
+ return this;
+ }
}
diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
index 0cb4624ad8a9a..f429a041e3d19 100644
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -2437,11 +2437,11 @@ public boolean isReflectivelyOpened(Module m, String pn, Module other) {
public Module addEnableNativeAccess(Module m) {
return m.implAddEnableNativeAccess();
}
- public void addEnableNativeAccessAllUnnamed() {
- Module.implAddEnableNativeAccessAllUnnamed();
+ public void addEnableNativeAccessToAllUnnamed() {
+ Module.implAddEnableNativeAccessToAllUnnamed();
}
- public boolean isEnableNativeAccess(Module m) {
- return m.implIsEnableNativeAccess();
+ public void ensureNativeAccess(Module m, Class> owner, String methodName) {
+ m.ensureNativeAccess(owner, methodName);
}
public ServicesCatalog getServicesCatalog(ModuleLayer layer) {
return layer.getServicesCatalog();
diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java
new file mode 100644
index 0000000000000..2fff341e65849
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/foreign/Arena.java
@@ -0,0 +1,166 @@
+/*
+ * 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;
+
+/**
+ * An arena controls the lifecycle of 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:
+ *
+ * {@snippet lang = java:
+ * try (Arena arena = Arena.openConfined()) {
+ * MemorySegment segment = MemorySegment.allocateNative(100, arena.scope());
+ * ...
+ * } // memory released here
+ *}
+ *
+ * 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:
+ *
+ * {@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());
+ * ...
+ * } // memory released here
+ *}
+ *
+ *
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
+ * 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
+ * by a thread other than the accessing thread, then ensuring correctness is much more complex. For example, a segment
+ * allocated with the arena might be accessed while another thread attempts, concurrently, to close the arena.
+ * To provide the strong temporal safety guarantee without forcing every client, even simple ones, to incur a performance
+ * 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}
+ * 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
+ * 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.
+ *
+ * @since 20
+ */
+@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
+public interface Arena extends SegmentAllocator, AutoCloseable {
+
+ /**
+ * 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 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,
+ * 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:
+ *
+ * {@snippet lang = java:
+ * S1.overlappingSlice(S2).isEmpty() == true
+ *}
+ *
+ * @param byteSize the size (in bytes) of the off-heap memory block backing the native memory segment.
+ * @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment.
+ * @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)
+ */
+ @Override
+ default MemorySegment allocate(long byteSize, long byteAlignment) {
+ return MemorySegment.allocateNative(byteSize, byteAlignment, scope());
+ }
+
+ /**
+ * {@return the arena scope}
+ */
+ SegmentScope scope();
+
+ /**
+ * Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain SegmentScope#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.
+ *
+ * @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()
+ *
+ * @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}.
+ */
+ @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 3d0a22239c6e9..3b6627d7f92c5 100644
--- a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java
+++ b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java
@@ -25,101 +25,38 @@
package java.lang.foreign;
import java.lang.invoke.MethodHandle;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.lang.invoke.MethodType;
import java.util.Objects;
import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
+import java.util.List;
+
+import jdk.internal.foreign.FunctionDescriptorImpl;
import jdk.internal.javac.PreviewFeature;
/**
- * A function descriptor is made up of zero or more argument layouts and zero or one return layout. A function descriptor
- * is used to model the signature of foreign functions when creating
- * {@linkplain Linker#downcallHandle(Addressable, FunctionDescriptor) downcall method handles} or
- * {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stubs}.
+ * 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}.
*
* @implSpec
- * This class is immutable, thread-safe and value-based.
+ * Implementing classes are immutable, thread-safe and value-based.
*
* @see MemoryLayout
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public sealed class FunctionDescriptor permits FunctionDescriptor.VariadicFunction {
-
- private final MemoryLayout resLayout;
- private final List argLayouts;
-
- private FunctionDescriptor(MemoryLayout resLayout, List argLayouts) {
- this.resLayout = resLayout;
- this.argLayouts = argLayouts;
- }
+public sealed interface FunctionDescriptor permits FunctionDescriptorImpl {
/**
* {@return the return layout (if any) associated with this function descriptor}
*/
- public Optional returnLayout() {
- return Optional.ofNullable(resLayout);
- }
+ Optional returnLayout();
/**
* {@return the argument layouts associated with this function descriptor (as an immutable list)}.
*/
- public List argumentLayouts() {
- return Collections.unmodifiableList(argLayouts);
- }
-
- /**
- * Creates a function descriptor with the given return and argument layouts.
- * @param resLayout the return layout.
- * @param argLayouts the argument layouts.
- * @return the new function descriptor.
- */
- public static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) {
- Objects.requireNonNull(resLayout);
- Objects.requireNonNull(argLayouts);
- Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
- return new FunctionDescriptor(resLayout, List.of(argLayouts));
- }
-
- /**
- * Creates a function descriptor with the given argument layouts and no return layout.
- * @param argLayouts the argument layouts.
- * @return the new function descriptor.
- */
- public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) {
- Objects.requireNonNull(argLayouts);
- Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
- return new FunctionDescriptor(null, List.of(argLayouts));
- }
-
- /**
- * Creates a specialized variadic function descriptor, by appending given variadic layouts to this
- * function descriptor argument layouts. The resulting function descriptor can report the position
- * of the {@linkplain #firstVariadicArgumentIndex() first variadic argument}, and cannot be altered
- * in any way: for instance, calling {@link #changeReturnLayout(MemoryLayout)} on the resulting descriptor
- * will throw an {@link UnsupportedOperationException}.
- * @param variadicLayouts the variadic argument layouts to be appended to this descriptor argument layouts.
- * @return a variadic function descriptor, or this descriptor if {@code variadicLayouts.length == 0}.
- */
- public FunctionDescriptor asVariadic(MemoryLayout... variadicLayouts) {
- Objects.requireNonNull(variadicLayouts);
- Arrays.stream(variadicLayouts).forEach(Objects::requireNonNull);
- return variadicLayouts.length == 0 ? this : new VariadicFunction(this, variadicLayouts);
- }
-
- /**
- * The index of the first variadic argument layout (where defined).
- * @return The index of the first variadic argument layout, or {@code -1} if this is not a
- * {@linkplain #asVariadic(MemoryLayout...) variadic} layout.
- */
- public int firstVariadicArgumentIndex() {
- return -1;
- }
+ List argumentLayouts();
/**
* Returns a function descriptor with the given argument layouts appended to the argument layout array
@@ -127,9 +64,7 @@ public int firstVariadicArgumentIndex() {
* @param addedLayouts the argument layouts to append.
* @return the new function descriptor.
*/
- public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) {
- return insertArgumentLayouts(argLayouts.size(), addedLayouts);
- }
+ FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts);
/**
* Returns a function descriptor with the given argument layouts inserted at the given index, into the argument
@@ -139,110 +74,57 @@ public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) {
* @return the new function descriptor.
* @throws IllegalArgumentException if {@code index < 0 || index > argumentLayouts().size()}.
*/
- public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) {
- if (index < 0 || index > argLayouts.size())
- throw new IllegalArgumentException("Index out of bounds: " + index);
- List added = List.of(addedLayouts); // null check on array and its elements
- List newLayouts = new ArrayList<>(argLayouts.size() + addedLayouts.length);
- newLayouts.addAll(argLayouts.subList(0, index));
- newLayouts.addAll(added);
- newLayouts.addAll(argLayouts.subList(index, argLayouts.size()));
- return new FunctionDescriptor(resLayout, newLayouts);
- }
+ FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts);
/**
* Returns a function descriptor with the given memory layout as the new return layout.
* @param newReturn the new return layout.
* @return the new function descriptor.
*/
- public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) {
- Objects.requireNonNull(newReturn);
- return new FunctionDescriptor(newReturn, argLayouts);
- }
+ FunctionDescriptor changeReturnLayout(MemoryLayout newReturn);
/**
* Returns a function descriptor with the return layout dropped. This is useful to model functions
* which return no values.
* @return the new function descriptor.
*/
- public FunctionDescriptor dropReturnLayout() {
- return new FunctionDescriptor(null, argLayouts);
- }
+ FunctionDescriptor dropReturnLayout();
/**
- * {@return the string representation of this function descriptor}
- */
- @Override
- public String toString() {
- return String.format("(%s)%s",
- IntStream.range(0, argLayouts.size())
- .mapToObj(i -> (i == firstVariadicArgumentIndex() ?
- "..." : "") + argLayouts.get(i))
- .collect(Collectors.joining()),
- returnLayout().map(Object::toString).orElse("v"));
- }
-
- /**
- * Compares the specified object with this function descriptor for equality. Returns {@code true} if and only if the specified
- * object is also a function descriptor, and all the following conditions are met:
+ * Returns the method type consisting of the carrier types of the layouts in this function descriptor.
+ *
+ * The carrier type of a layout is determined as follows:
*
- *
the two function descriptors have equals return layouts (see {@link MemoryLayout#equals(Object)}), or both have no return layout;
- *
the two function descriptors have argument layouts that are pair-wise {@linkplain MemoryLayout#equals(Object) equal}; and
- *
the two function descriptors have the same leading {@linkplain #firstVariadicArgumentIndex() variadic argument index}
+ *
If the layout is a {@link ValueLayout} the carrier type is determined through {@link ValueLayout#carrier()}.
+ *
If the layout is a {@link GroupLayout} the carrier type is {@link MemorySegment}.
+ *
If the layout is a {@link PaddingLayout}, or {@link SequenceLayout} an {@link IllegalArgumentException} is thrown.
*
*
- * @param other the object to be compared for equality with this function descriptor.
- * @return {@code true} if the specified object is equal to this function descriptor.
+ * @return the method type consisting of the carrier types of the layouts in this function descriptor
+ * @throws IllegalArgumentException if one or more layouts in the function descriptor can not be mapped to carrier
+ * types (e.g. if they are sequence layouts or padding layouts).
*/
- @Override
- public boolean equals(Object other) {
- return other instanceof FunctionDescriptor f &&
- Objects.equals(resLayout, f.resLayout) &&
- Objects.equals(argLayouts, f.argLayouts) &&
- firstVariadicArgumentIndex() == f.firstVariadicArgumentIndex();
- }
+ MethodType toMethodType();
/**
- * {@return the hash code value for this function descriptor}
+ * Creates a function descriptor with the given return and argument layouts.
+ * @param resLayout the return layout.
+ * @param argLayouts the argument layouts.
+ * @return the new function descriptor.
*/
- @Override
- public int hashCode() {
- return Objects.hash(argLayouts, resLayout, firstVariadicArgumentIndex());
+ static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) {
+ Objects.requireNonNull(resLayout);
+ // Null checks are implicit in List.of(argLayouts)
+ return FunctionDescriptorImpl.of(resLayout, List.of(argLayouts));
}
- static final class VariadicFunction extends FunctionDescriptor {
-
- private final int firstVariadicIndex;
-
- public VariadicFunction(FunctionDescriptor descriptor, MemoryLayout... argLayouts) {
- super(descriptor.returnLayout().orElse(null),
- Stream.concat(descriptor.argumentLayouts().stream(), Stream.of(argLayouts)).toList());
- this.firstVariadicIndex = descriptor.argumentLayouts().size();
- }
-
- @Override
- public int firstVariadicArgumentIndex() {
- return firstVariadicIndex;
- }
-
- @Override
- public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public FunctionDescriptor dropReturnLayout() {
- throw new UnsupportedOperationException();
- }
+ /**
+ * Creates a function descriptor with the given argument layouts and no return layout.
+ * @param argLayouts the argument layouts.
+ * @return the new function descriptor.
+ */
+ static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) {
+ // Null checks are implicit in List.of(argLayouts)
+ return FunctionDescriptorImpl.ofVoid(List.of(argLayouts));
}
}
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 d012735ba9f8d..56ace4ee13513 100644
--- a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java
+++ b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java
@@ -25,19 +25,14 @@
*/
package java.lang.foreign;
-import java.util.Collections;
import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.LongBinaryOperator;
-import java.util.stream.Collectors;
import jdk.internal.javac.PreviewFeature;
/**
* A compound layout that aggregates multiple member layouts. There are two ways in which member layouts
- * can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a struct
+ * can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a struct layout
* (see {@link MemoryLayout#structLayout(MemoryLayout...)}); conversely, if all member layouts are laid out at the same starting offset,
- * the resulting group layout is said to be a union (see {@link MemoryLayout#unionLayout(MemoryLayout...)}).
+ * the resulting group layout is said to be a union layout (see {@link MemoryLayout#unionLayout(MemoryLayout...)}).
*
* @implSpec
* This class is immutable, thread-safe and value-based.
@@ -45,55 +40,7 @@
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public final class GroupLayout extends AbstractLayout implements MemoryLayout {
-
- /**
- * The group kind.
- */
- enum Kind {
- /**
- * A 'struct' kind.
- */
- STRUCT("", Math::addExact),
- /**
- * A 'union' kind.
- */
- UNION("|", Math::max);
-
- final String delimTag;
- final LongBinaryOperator sizeOp;
-
- Kind(String delimTag, LongBinaryOperator sizeOp) {
- this.delimTag = delimTag;
- this.sizeOp = sizeOp;
- }
-
- long sizeof(List 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
- }
- }
-
- private final Kind kind;
- private final List elements;
-
- GroupLayout(Kind kind, List elements) {
- this(kind, elements, kind.alignof(elements), Optional.empty());
- }
-
- GroupLayout(Kind kind, List elements, long alignment, Optional name) {
- super(kind.sizeof(elements), alignment, name);
- this.kind = kind;
- this.elements = elements;
- }
+public sealed interface GroupLayout extends MemoryLayout permits StructLayout, UnionLayout {
/**
* Returns the member layouts associated with this group.
@@ -104,84 +51,17 @@ long alignof(List elems) {
*
* @return the member layouts associated with this group.
*/
- public List memberLayouts() {
- return Collections.unmodifiableList(elements);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return decorateLayoutString(elements.stream()
- .map(Object::toString)
- .collect(Collectors.joining(kind.delimTag, "[", "]")));
- }
-
- /**
- * {@return {@code true}, if this group layout is a struct layout}
- */
- public boolean isStruct() {
- return kind == Kind.STRUCT;
- }
-
- /**
- * {@return {@code true}, if this group layout is a union layout}
- */
- public boolean isUnion() {
- return kind == Kind.UNION;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (!super.equals(other)) {
- return false;
- }
- return other instanceof GroupLayout otherGroup &&
- kind == otherGroup.kind &&
- elements.equals(otherGroup.elements);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), kind, elements);
- }
-
- @Override
- GroupLayout dup(long alignment, Optional name) {
- return new GroupLayout(kind, elements, alignment, name);
- }
-
- @Override
- boolean hasNaturalAlignment() {
- return alignment == kind.alignof(elements);
- }
-
- //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
- //but that causes issues with javadoc, see JDK-8224052
+ List memberLayouts();
/**
* {@inheritDoc}
*/
@Override
- public GroupLayout withName(String name) {
- return (GroupLayout)super.withName(name);
- }
+ GroupLayout withName(String name);
/**
* {@inheritDoc}
*/
@Override
- public GroupLayout withBitAlignment(long alignmentBits) {
- return (GroupLayout)super.withBitAlignment(alignmentBits);
- }
+ 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 53196ab2bd0c5..c8fa1b2320fa0 100644
--- a/src/java.base/share/classes/java/lang/foreign/Linker.java
+++ b/src/java.base/share/classes/java/lang/foreign/Linker.java
@@ -26,13 +26,13 @@
package java.lang.foreign;
import jdk.internal.foreign.abi.AbstractLinker;
+import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
/**
* A linker provides access to foreign functions from Java code, and access to Java code from foreign functions.
@@ -47,9 +47,9 @@
* in the JVM and foreign functions in the library. In particular:
*
*
A linker allows Java code to link against foreign functions, via
- * {@linkplain #downcallHandle(Addressable, FunctionDescriptor) downcall method handles}; and
A linker allows foreign functions to call Java method handles,
- * via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stubs}.
+ * via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) 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.
@@ -62,29 +62,16 @@
*
*
Downcall method handles
*
- * {@linkplain #downcallHandle(FunctionDescriptor) Linking a foreign function} is a process which requires a function descriptor,
+ * {@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 #downcallType(FunctionDescriptor) derived} from the argument and return layouts in the function descriptor.
- * More specifically, given each layout {@code L} in the function descriptor, a corresponding carrier {@code C} is inferred,
- * as described below:
+ * {@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 {@code L} is a {@link ValueLayout} with carrier {@code E} then there are two cases:
- *
- *
if {@code L} occurs in a parameter position and {@code E} is {@code MemoryAddress.class},
- * then {@code C = Addressable.class};
- *
otherwise, {@code C = E};
- *
- *
or, if {@code L} is a {@link GroupLayout}, then {@code C} is set to {@code MemorySegment.class}
- *
- *
- * The downcall method handle type, derived as above, might be decorated by additional leading parameters,
- * in the given order if both are present:
- *
- *
If the downcall method handle is created {@linkplain #downcallHandle(FunctionDescriptor) without specifying a target address},
- * the downcall method handle type features a leading parameter of type {@link Addressable}, from which the
+ *
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
@@ -93,26 +80,15 @@
*
*
Upcall stubs
*
- * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) Creating an upcall stub} requires a method
+ * {@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.
*
- * The type of the provided method handle has to {@linkplain #upcallType(FunctionDescriptor) match} the Java
- * {@linkplain java.lang.invoke.MethodType method type} associated with the upcall stub, which is derived from the argument
- * and return layouts in the function descriptor. More specifically, given each layout {@code L} in the function descriptor,
- * a corresponding carrier {@code C} is inferred, as described below:
- *
- *
If {@code L} is a {@link ValueLayout} with carrier {@code E} then there are two cases:
- *
- *
If {@code L} occurs in a return position and {@code E} is {@code MemoryAddress.class},
- * then {@code C = Addressable.class};
- *
Otherwise, {@code C = E};
- *
- *
Or, if {@code L} is a {@link GroupLayout}, then {@code C} is set to {@code MemorySegment.class}
- *
+ * 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.
+ *
* Upcall stubs are modelled by instances of type {@link MemorySegment}; upcall stubs can be passed by reference to other
- * downcall method handles (as {@link MemorySegment} implements the {@link Addressable} interface) and,
- * when no longer required, they can be {@linkplain MemorySession#close() released}, via their associated {@linkplain MemorySession session}.
+ * downcall method handles and, they are released via their associated {@linkplain SegmentScope scope}.
*
*
Safety considerations
*
@@ -121,23 +97,34 @@
* 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 that is a memory resource {@code R} (of type {@link MemorySegment}
- * or {@link VaList}):
+ * the linker runtime guarantees the following for any argument {@code A} of type {@link MemorySegment} whose corresponding
+ * layout is {@link ValueLayout#ADDRESS}:
*
- *
The memory session of {@code R} is {@linkplain MemorySession#isAlive() alive}. Otherwise, the invocation throws
+ *
The scope of {@code A} is {@linkplain SegmentScope#isAlive() alive}. Otherwise, the invocation throws
* {@link IllegalStateException};
- *
The invocation occurs in same thread as the one {@linkplain MemorySession#ownerThread() owning} the memory session of {@code R},
- * if said session is confined. Otherwise, the invocation throws {@link WrongThreadException}; and
- *
The memory session of {@code R} is {@linkplain MemorySession#whileAlive(Runnable) kept alive} (and cannot be closed) during the invocation.
+ *
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}.
*
* 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,
* if the foreign code casts the function pointer associated with an upcall stub to a type
* that is incompatible with the provided function descriptor. Moreover, if the target method
- * handle associated with an upcall stub returns a {@linkplain MemoryAddress memory address}, clients must ensure
+ * 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.
@@ -159,22 +146,22 @@ public sealed interface Linker permits AbstractLinker {
* 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 constraints 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 MemoryAddress}.
+ * 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(Addressable, FunctionDescriptor) downcall method handle} or an
- * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stub}.
+ * {@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 a {@linkplain FunctionDescriptor#asVariadic(MemoryLayout...) variadic}
- * function descriptor, in which the specialized signature of a given variable arity callsite is described in full.
- * 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}).
+ * 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
@@ -199,55 +186,60 @@ static Linker nativeLinker() {
}
/**
- * Creates a method handle which can be used to call a target foreign function with the given signature and address.
+ * 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 runtime
- * to allocate structs returned by-value.
+ * an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker to allocate
+ * structs returned by-value.
*
* Calling this method is equivalent to the following code:
* {@snippet lang=java :
* linker.downcallHandle(function).bindTo(symbol);
* }
*
- * @param symbol the address of the target function.
+ * @param symbol the address of the target function.
* @param function the function descriptor of the target function.
+ * @param options any linker options.
* @return a downcall method handle. The method handle type is inferred
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
- * or if the symbol is {@link MemoryAddress#NULL}
+ * or if the symbol is {@link MemorySegment#NULL}
+ * @throws IllegalArgumentException if an invalid combination of linker options is given.
*/
- default MethodHandle downcallHandle(Addressable symbol, FunctionDescriptor function) {
+ default MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) {
SharedUtils.checkSymbol(symbol);
- return downcallHandle(function).bindTo(symbol);
+ return downcallHandle(function, options).bindTo(symbol);
}
/**
- * Creates a method handle which can be used to call a target foreign function with the given signature.
+ * 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 Addressable}, which is used to specify the address of the target function
+ * entry point, of type {@link MemorySegment}, which is used to specify the address of the target function
* to be called.
*
* 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 runtime to allocate structs returned by-value.
+ * which will be used by the linker to allocate structs returned by-value.
*
- * The returned method handle will throw an {@link IllegalArgumentException} if the {@link Addressable} parameter passed to it is
- * associated with the {@link MemoryAddress#NULL} address, or a {@link NullPointerException} if that parameter is {@code null}.
+ * 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}.
*
* @param function the function descriptor of the target function.
+ * @param options any linker options.
* @return a downcall method handle. The method handle type is inferred
* from the provided function descriptor.
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
+ * @throws IllegalArgumentException if an invalid combination of linker options is given.
*/
- MethodHandle downcallHandle(FunctionDescriptor function);
+ MethodHandle downcallHandle(FunctionDescriptor function, Option... options);
/**
- * Creates a stub which can be passed to other foreign functions as a function pointer, with the given
- * memory session. Calling such a function pointer from foreign code will result in the execution of the provided
+ * 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
* method handle.
*
- * The returned memory segment's base address points to the newly allocated upcall stub, and is associated with
- * the provided memory session. When such session is closed, the corresponding upcall stub will be deallocated.
+ * 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 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
@@ -257,16 +249,16 @@ default MethodHandle downcallHandle(Addressable symbol, FunctionDescriptor funct
*
* @param target the target method handle.
* @param function the upcall stub function descriptor.
- * @param session the upcall stub memory session.
- * @return a zero-length segment whose base address is the address of the upcall stub.
+ * @param scope the scope associated with the returned upcall stub segment.
+ * @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 session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
+ * @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}.
*/
- MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, MemorySession session);
+ MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope);
/**
* Returns a symbol lookup for symbols in a set of commonly used libraries.
@@ -284,22 +276,21 @@ default MethodHandle downcallHandle(Addressable symbol, FunctionDescriptor funct
SymbolLookup defaultLookup();
/**
- * {@return the downcall method handle {@linkplain MethodType type} associated with the given function descriptor}
- * @param functionDescriptor a function descriptor.
- * @throws IllegalArgumentException if one or more layouts in the function descriptor are not supported
- * (e.g. if they are sequence layouts or padding layouts).
+ * A linker option is used to indicate additional linking requirements to the linker,
+ * besides what is described by a function descriptor.
+ * @since 20
*/
- static MethodType downcallType(FunctionDescriptor functionDescriptor) {
- return SharedUtils.inferMethodType(functionDescriptor, false);
- }
+ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
+ sealed interface Option
+ permits LinkerOptions.FirstVariadicArg {
- /**
- * {@return the method handle {@linkplain MethodType type} associated with an upcall stub with the given function descriptor}
- * @param functionDescriptor a function descriptor.
- * @throws IllegalArgumentException if one or more layouts in the function descriptor are not supported
- * (e.g. if they are sequence layouts or padding layouts).
- */
- static MethodType upcallType(FunctionDescriptor functionDescriptor) {
- return SharedUtils.inferMethodType(functionDescriptor, true);
+ /**
+ * {@return a linker option used to denote the index of the first variadic argument layout in a
+ * foreign function call}
+ * @param index the index of the first variadic argument in a downcall handle linkage request.
+ */
+ static Option firstVariadicArg(int index) {
+ return new LinkerOptions.FirstVariadicArg(index);
+ }
}
}
diff --git a/src/java.base/share/classes/java/lang/foreign/MemoryAddress.java b/src/java.base/share/classes/java/lang/foreign/MemoryAddress.java
deleted file mode 100644
index cc4cd949d6432..0000000000000
--- a/src/java.base/share/classes/java/lang/foreign/MemoryAddress.java
+++ /dev/null
@@ -1,854 +0,0 @@
-/*
- * Copyright (c) 2019, 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.nio.ByteOrder;
-
-import jdk.internal.foreign.MemoryAddressImpl;
-import jdk.internal.javac.PreviewFeature;
-import jdk.internal.reflect.CallerSensitive;
-
-import java.lang.invoke.MethodHandle;
-
-/**
- * A memory address models a reference into a memory location. Memory addresses are typically obtained in one of the following ways:
- *
- *
By calling {@link Addressable#address()} on an instance of type {@link Addressable} (e.g. a memory segment);
- *
By invoking a {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle} which returns a pointer;
- *
By reading an address from memory, e.g. via {@link MemorySegment#get(ValueLayout.OfAddress, long)}; and
- *
By the invocation of an {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stub} which accepts a pointer.
- *
- * A memory address is backed by a raw machine pointer, expressed as a {@linkplain #toRawLongValue() long value}.
- *
- *
Dereferencing memory addresses
- *
- * A memory address can be read or written using various methods provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}).
- * Each dereference method takes a {@linkplain ValueLayout value layout}, which specifies the size,
- * alignment constraints, byte order as well as the Java type associated with the dereference operation, and an offset.
- * For instance, to read an int from a segment, using {@linkplain ByteOrder#nativeOrder() default endianness}, the following code can be used:
- * {@snippet lang=java :
- * MemoryAddress address = ...
- * int value = address.get(ValueLayout.JAVA_INT, 0);
- * }
- *
- * If the value to be read is stored in memory using {@link ByteOrder#BIG_ENDIAN big-endian} encoding, the dereference operation
- * can be expressed as follows:
- * {@snippet lang=java :
- * MemoryAddress address = ...
- * int value = address.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
- * }
- *
- * All the dereference methods in this class are restricted: since
- * a memory address does not feature temporal nor spatial bounds, the runtime has no way to check the correctness
- * of the memory dereference operation.
- *
- * @implSpec
- * Implementations of this interface are immutable, thread-safe and value-based.
- *
- * @since 19
- */
-@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public sealed interface MemoryAddress extends Addressable permits MemoryAddressImpl {
-
- /**
- * {@return the raw long value associated with this memory address}
- */
- long toRawLongValue();
-
- /**
- * Returns a memory address at given offset from this address.
- * @param offset specified offset (in bytes), relative to this address, which should be used to create the new address.
- * Might be negative.
- * @return a memory address with the given offset from current one.
- */
- MemoryAddress addOffset(long offset);
-
- /**
- * Reads a UTF-8 encoded, null-terminated string from this address at the given offset.
- *
- * This method always replaces malformed-input and unmappable-character
- * sequences with this charset's default replacement string. The {@link
- * java.nio.charset.CharsetDecoder} class should be used when more control
- * over the decoding process is required.
- *
- * 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 offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a Java string constructed from the bytes read from the given starting address ({@code toRawLongValue() + offset})
- * up to (but not including) the first {@code '\0'} terminator character (assuming one is found).
- * @throws IllegalArgumentException if the size of the UTF-8 string is greater than the largest string supported by the platform.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- String getUtf8String(long offset);
-
- /**
- * Writes the given string to this address at the given offset, converting it to a null-terminated byte sequence using UTF-8 encoding.
- *
- * This method always replaces malformed-input and unmappable-character
- * sequences with this charset's default replacement string. The {@link
- * java.nio.charset.CharsetDecoder} class should be used when more control
- * over the decoding process is required.
- *
- * 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 offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @param str the Java string to be written at this address.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setUtf8String(long offset, String str);
-
- /**
- * Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
- * object is also an address, and it refers to the same memory location as this address.
- *
- * @param that the object to be compared for equality with this address.
- * @return {@code true} if the specified object is equal to this address.
- */
- @Override
- boolean equals(Object that);
-
- /**
- * {@return the hash code value for this address}
- */
- @Override
- int hashCode();
-
- /**
- * The memory address instance modelling the {@code NULL} address.
- */
- MemoryAddress NULL = new MemoryAddressImpl(0L);
-
- /**
- * Creates a memory address from the given long value.
- * @param value the long value representing a raw address.
- * @return a memory address with the given raw long value.
- */
- static MemoryAddress ofLong(long value) {
- return value == 0 ?
- NULL :
- new MemoryAddressImpl(value);
- }
-
- /**
- * Reads a byte from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a byte value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- byte get(ValueLayout.OfByte layout, long offset);
-
- /**
- * Writes a byte into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the byte value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfByte layout, long offset, byte value);
-
- /**
- * Reads a boolean from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a boolean value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- boolean get(ValueLayout.OfBoolean layout, long offset);
-
- /**
- * Writes a boolean into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the boolean value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfBoolean layout, long offset, boolean value);
-
- /**
- * Reads a char from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a char value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- char get(ValueLayout.OfChar layout, long offset);
-
- /**
- * Writes a char into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the char value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfChar layout, long offset, char value);
-
- /**
- * Reads a short from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a short value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- short get(ValueLayout.OfShort layout, long offset);
-
- /**
- * Writes a short into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the short value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfShort layout, long offset, short value);
-
- /**
- * Reads an int from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return an int value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- int get(ValueLayout.OfInt layout, long offset);
-
- /**
- * Writes an int into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the int value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfInt layout, long offset, int value);
-
- /**
- * Reads a float from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a float value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- float get(ValueLayout.OfFloat layout, long offset);
-
- /**
- * Writes a float into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the float value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfFloat layout, long offset, float value);
-
- /**
- * Reads a long from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a long value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- long get(ValueLayout.OfLong layout, long offset);
-
- /**
- * Writes a long into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the long value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfLong layout, long offset, long value);
-
- /**
- * Reads a double from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return a double value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- double get(ValueLayout.OfDouble layout, long offset);
-
- /**
- * Writes a double into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the double value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfDouble layout, long offset, double value);
-
- /**
- * Reads an address from this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be read.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
- * @return an address value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- MemoryAddress get(ValueLayout.OfAddress layout, long offset);
-
- /**
- * Writes an address into this address at the given offset, with the given layout.
- *
- * 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 layout of the memory region to be written.
- * @param offset offset in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
- * @param value the address value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void set(ValueLayout.OfAddress layout, long offset, Addressable value);
-
- /**
- * Reads a char from this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be read.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @return a char value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- char getAtIndex(ValueLayout.OfChar layout, long index);
-
- /**
- * Writes a char into this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be written.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @param value the char value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setAtIndex(ValueLayout.OfChar layout, long index, char value);
-
- /**
- * Reads a short from this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be read.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @return a short value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- short getAtIndex(ValueLayout.OfShort layout, long index);
-
- /**
- * Writes a short into this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be written.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @param value the short value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setAtIndex(ValueLayout.OfShort layout, long index, short value);
-
- /**
- * Reads an int from this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be read.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @return an int value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- int getAtIndex(ValueLayout.OfInt layout, long index);
-
- /**
- * Writes an int into this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be written.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @param value the int value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setAtIndex(ValueLayout.OfInt layout, long index, int value);
-
- /**
- * Reads a float from this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be read.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @return a float value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- float getAtIndex(ValueLayout.OfFloat layout, long index);
-
- /**
- * Writes a float into this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be written.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @param value the float value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setAtIndex(ValueLayout.OfFloat layout, long index, float value);
-
- /**
- * Reads a long from this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be read.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @return a long value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- long getAtIndex(ValueLayout.OfLong layout, long index);
-
- /**
- * Writes a long into this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be written.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @param value the long value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setAtIndex(ValueLayout.OfLong layout, long index, long value);
-
- /**
- * Reads a double from this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be read.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @return a double value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- double getAtIndex(ValueLayout.OfDouble layout, long index);
-
- /**
- * Writes a double into this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be written.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @param value the double value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setAtIndex(ValueLayout.OfDouble layout, long index, double value);
-
- /**
- * Reads an address from this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be read.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @return an address value read from this address.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index);
-
- /**
- * Writes an address into this address at the given index, scaled by the given layout 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 layout the layout of the memory region to be written.
- * @param index index in bytes (relative to this address). Might be negative.
- * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
- * @param value the address value to be written.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
- * or if the layout alignment is greater than its size.
- * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
- * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
- * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
- */
- @CallerSensitive
- void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value);
-}
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 fe953d2aa3c4a..8d56202a04af4 100644
--- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java
+++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java
@@ -35,15 +35,20 @@
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.foreign.LayoutPath;
import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind;
import jdk.internal.foreign.Utils;
+import jdk.internal.foreign.layout.MemoryLayoutUtil;
+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;
/**
- * A memory layout can be used to describe the contents of a memory segment.
+ * A memory layout describes the contents of a memory segment.
* There are two leaves in the layout hierarchy, value layouts, which are used to represent values of given size and kind (see
* {@link ValueLayout}) and padding layouts which are used, as the name suggests, to represent a portion of a memory
* segment whose contents should be ignored, and which are primarily present for alignment reasons (see {@link MemoryLayout#paddingLayout(long)}).
@@ -162,10 +167,11 @@
* @implSpec
* Implementations of this interface are immutable, thread-safe and value-based.
*
+ * @sealedGraph
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, GroupLayout, PaddingLayout, ValueLayout {
+public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, PaddingLayout, ValueLayout {
/**
* {@return the layout size, in bits}
@@ -185,7 +191,7 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
Optional name();
/**
- * Returns a memory layout with the same size and alignment constraints as this layout,
+ * Returns a memory layout of the same type with the same size and alignment constraint as this layout,
* but with the specified name.
*
* @param name the layout name.
@@ -235,11 +241,11 @@ default long byteAlignment() {
}
/**
- * Returns a memory layout with the same size and name as this layout,
- * but with the specified alignment constraints (in bits).
+ * Returns a memory layout of the same type with the same size and name as this layout,
+ * but with the specified alignment constraint (in bits).
*
* @param bitAlignment the layout alignment constraint, expressed in bits.
- * @return a memory layout with the given alignment constraints.
+ * @return a memory layout with the given alignment constraint.
* @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than 8.
*/
MemoryLayout withBitAlignment(long bitAlignment);
@@ -307,7 +313,7 @@ default MethodHandle bitOffsetHandle(PathElement... elements) {
* in {@code elements} is {@code null}.
*/
default long byteOffset(PathElement... elements) {
- return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.bitsToBytesThrowOffset);
+ return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.BITS_TO_BYTES_THROW_OFFSET);
}
/**
@@ -343,23 +349,24 @@ default long byteOffset(PathElement... elements) {
*/
default MethodHandle byteOffsetHandle(PathElement... elements) {
MethodHandle mh = bitOffsetHandle(elements);
- mh = MethodHandles.filterReturnValue(mh, Utils.MH_bitsToBytesOrThrowForOffset);
+ mh = MethodHandles.filterReturnValue(mh, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET);
return mh;
}
/**
- * Creates an access var handle that can be used to dereference memory at the layout selected by the given layout path,
+ * Creates a var handle that can be used to access a memory segment at the layout selected by the given layout path,
* where the path is considered rooted in this layout.
*
- * The final memory location accessed by the returned var handle can be computed as follows:
+ * The final address accessed by the returned var handle can be computed as follows:
*
*
*
- * where {@code base} denotes the base address expressed by the {@link MemorySegment} access coordinate
- * (see {@link MemorySegment#address()} and {@link MemoryAddress#toRawLongValue()}) and {@code offset}
- * can be expressed in the following form:
+ * Where {@code base(segment)} denotes a function that returns the physical base address of the accessed
+ * memory segment. For native segments, this function just returns the native segment's
+ * {@linkplain MemorySegment#address() address}. For heap segments, this function is more complex, as the address
+ * of heap segments is virtualized. The {@code offset} coordinate can be expressed in the following form:
*
*
{@code
* offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
@@ -378,8 +385,8 @@ default MethodHandle byteOffsetHandle(PathElement... elements) {
* features certain access mode restrictions, which are common to all memory segment view handles.
*
* @param elements the layout path elements.
- * @return a var handle which can be used to dereference memory 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 constraints.
+ * @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}).
* @see MethodHandles#memorySegmentViewVarHandle(ValueLayout)
*/
@@ -458,11 +465,6 @@ private static Z computePathOp(LayoutPath path, Function fina
return finalizer.apply(path);
}
- /**
- * {@return true, if this layout is a padding layout}
- */
- boolean isPadding();
-
/**
* An element in a layout path. There
* are two kinds of path elements: group path elements and sequence path elements. Group
@@ -581,15 +583,15 @@ static PathElement sequenceElement() {
/**
* Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified
* object is also a layout, and it is equal to this layout. Two layouts are considered equal if they are of
- * the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
+ * the same kind, have the same size, name and alignment constraint. Furthermore, depending on the layout kind, additional
* conditions must be satisfied:
*
*
two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order},
* and {@linkplain ValueLayout#carrier() carrier}
*
two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal
- *
two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
- * {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
+ *
two group layouts are considered equal if they are of the same type (see {@link StructLayout},
+ * {@link UnionLayout}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
*
*
* @param other the object to be compared for equality with this layout.
@@ -615,9 +617,9 @@ static PathElement sequenceElement() {
* @return the new selector layout.
* @throws IllegalArgumentException if {@code size <= 0}.
*/
- static MemoryLayout paddingLayout(long size) {
- AbstractLayout.checkSize(size);
- return new PaddingLayout(size);
+ static PaddingLayout paddingLayout(long size) {
+ MemoryLayoutUtil.checkSize(size);
+ return PaddingLayoutImpl.of(size);
}
/**
@@ -632,7 +634,7 @@ static MemoryLayout paddingLayout(long size) {
*
{@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 MemoryAddress.class}
+ *
{@link ValueLayout.OfAddress}, for {@code MemorySegment.class}
*
* @param carrier the value layout carrier.
* @param order the value layout's byte order.
@@ -643,54 +645,58 @@ static ValueLayout valueLayout(Class> carrier, ByteOrder order) {
Objects.requireNonNull(carrier);
Objects.requireNonNull(order);
if (carrier == boolean.class) {
- return new ValueLayout.OfBoolean(order);
+ return ValueLayouts.OfBooleanImpl.of(order);
} else if (carrier == char.class) {
- return new ValueLayout.OfChar(order);
+ return ValueLayouts.OfCharImpl.of(order);
} else if (carrier == byte.class) {
- return new ValueLayout.OfByte(order);
+ return ValueLayouts.OfByteImpl.of(order);
} else if (carrier == short.class) {
- return new ValueLayout.OfShort(order);
+ return ValueLayouts.OfShortImpl.of(order);
} else if (carrier == int.class) {
- return new ValueLayout.OfInt(order);
+ return ValueLayouts.OfIntImpl.of(order);
} else if (carrier == float.class) {
- return new ValueLayout.OfFloat(order);
+ return ValueLayouts.OfFloatImpl.of(order);
} else if (carrier == long.class) {
- return new ValueLayout.OfLong(order);
+ return ValueLayouts.OfLongImpl.of(order);
} else if (carrier == double.class) {
- return new ValueLayout.OfDouble(order);
- } else if (carrier == MemoryAddress.class) {
- return new ValueLayout.OfAddress(order);
+ return ValueLayouts.OfDoubleImpl.of(order);
+ } else if (carrier == MemorySegment.class) {
+ return ValueLayouts.OfAddressImpl.of(order);
} else {
throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName());
}
}
/**
- * Creates a sequence layout with the given element layout and element count. If the element count has the
- * special value {@code -1}, the element count is inferred to be the biggest possible count such that
- * the sequence layout size does not overflow, using the following formula:
- *
- *
+ * Creates a sequence layout with the given element layout and element count.
*
- * @param elementCount the sequence element count; if set to {@code -1}, the sequence element count is inferred.
+ * @param elementCount the sequence element count.
* @param elementLayout the sequence element layout.
* @return the new sequence layout with the given element layout and size.
- * @throws IllegalArgumentException if {@code elementCount < -1}.
- * @throws IllegalArgumentException if {@code elementCount != -1} and the computation {@code elementCount * elementLayout.bitSize()} overflows.
+ * @throws IllegalArgumentException if {@code elementCount } is negative.
*/
static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) {
- if (elementCount == -1) {
- // inferred element count
- long inferredElementCount = Long.MAX_VALUE / elementLayout.bitSize();
- return new SequenceLayout(inferredElementCount, elementLayout);
- } else {
- // explicit element count
- AbstractLayout.checkSize(elementCount, true);
- return wrapOverflow(() ->
- new SequenceLayout(elementCount, Objects.requireNonNull(elementLayout)));
- }
+ MemoryLayoutUtil.checkSize(elementCount, true);
+ Objects.requireNonNull(elementLayout);
+ return wrapOverflow(() ->
+ SequenceLayoutImpl.of(elementCount, elementLayout));
+ }
+
+ /**
+ * Creates a sequence layout with the given element layout and the maximum element
+ * count such that it does not overflow a {@code long}.
+ *
+ * This is equivalent to the following code:
+ * {@snippet lang = java:
+ * sequenceLayout(Long.MAX_VALUE / elementLayout.bitSize(), elementLayout);
+ * }
+ *
+ * @param elementLayout the sequence element layout.
+ * @return a new sequence layout with the given element layout and maximum element count.
+ */
+ static SequenceLayout sequenceLayout(MemoryLayout elementLayout) {
+ Objects.requireNonNull(elementLayout);
+ return sequenceLayout(Long.MAX_VALUE / elementLayout.bitSize(), elementLayout);
}
/**
@@ -701,13 +707,12 @@ static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayo
* @throws IllegalArgumentException if the sum of the {@linkplain #bitSize() bit sizes} of the member layouts
* overflows.
*/
- static GroupLayout structLayout(MemoryLayout... elements) {
+ static StructLayout structLayout(MemoryLayout... elements) {
Objects.requireNonNull(elements);
return wrapOverflow(() ->
- new GroupLayout(GroupLayout.Kind.STRUCT,
- Stream.of(elements)
- .map(Objects::requireNonNull)
- .collect(Collectors.toList())));
+ StructLayoutImpl.of(Stream.of(elements)
+ .map(Objects::requireNonNull)
+ .toList()));
}
/**
@@ -716,12 +721,11 @@ static GroupLayout structLayout(MemoryLayout... elements) {
* @param elements The member layouts of the union layout.
* @return a union layout with the given member layouts.
*/
- static GroupLayout unionLayout(MemoryLayout... elements) {
+ static UnionLayout unionLayout(MemoryLayout... elements) {
Objects.requireNonNull(elements);
- return new GroupLayout(GroupLayout.Kind.UNION,
- Stream.of(elements)
- .map(Objects::requireNonNull)
- .collect(Collectors.toList()));
+ return UnionLayoutImpl.of(Stream.of(elements)
+ .map(Objects::requireNonNull)
+ .toList());
}
private static L wrapOverflow(Supplier layoutSupplier) {
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 70a52ba37d774..76c2de64242cb 100644
--- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java
+++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java
@@ -27,13 +27,15 @@
package java.lang.foreign;
import java.io.UncheckedIOException;
-import java.lang.reflect.Array;
import java.lang.invoke.MethodHandles;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
+import java.nio.channels.FileChannel.*;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
@@ -43,75 +45,91 @@
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.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.ForceInline;
/**
- * A memory segment models a contiguous region of memory. A memory segment is associated with both spatial
- * and temporal bounds (e.g. a {@link MemorySession}). Spatial bounds ensure that memory access operations on a memory segment cannot affect a memory location
- * which falls outside the boundaries of the memory segment being accessed. Temporal bounds ensure that memory access
- * operations on a segment cannot occur after the memory session associated with a memory segment has been closed (see {@link MemorySession#close()}).
- *
- * There are many kinds of memory segments:
+ * A memory segment provides access to a contiguous region of memory.
+ *
{@linkplain FileChannel#map(FileChannel.MapMode, long, long, MemorySession) mapped memory segments}, obtained by mapping
- * a file into main memory ({@code mmap}); the contents of a mapped memory segments can be {@linkplain #force() persisted} and
- * {@linkplain #load() loaded} to and from the underlying memory-mapped file;
- *
{@linkplain MemorySegment#ofArray(int[]) array segments}, wrapping an existing, heap-allocated Java array; and
- *
{@linkplain MemorySegment#ofBuffer(Buffer) buffer segments}, wrapping an existing {@link Buffer} instance;
- * buffer memory segments might be backed by either off-heap memory or on-heap memory, depending on the characteristics of the
- * wrapped buffer instance. For instance, a buffer memory segment obtained from a byte buffer created with the
- * {@link ByteBuffer#allocateDirect(int)} method will be backed by off-heap memory.
+ *
A heap segment is backed by, and provides access to, a region of memory inside the Java heap (an "on-heap" region).
+ *
A native segment is backed by, and provides access to, a region of memory outside the Java heap (an "off-heap" region).
*
+ * 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)}
+ * 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
+ * (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.
+ *
+ * Both kinds of segments are read and written using the same methods, known as access operations.
+ * An access operation on a memory segment always and only provides access to the region for which the segment was obtained.
*
- *
Lifecycle and confinement
+ *
Characteristics of memory segments
*
- * Memory segments are associated with a {@linkplain MemorySegment#session() memory session}. As for all resources associated
- * with a memory session, a segment cannot be accessed after its underlying session has been closed. For instance,
- * the following code will result in an exception:
- * {@snippet lang=java :
- * MemorySegment segment = null;
- * try (MemorySession session = MemorySession.openConfined()) {
- * segment = MemorySegment.allocateNative(8, session);
- * }
- * segment.get(ValueLayout.JAVA_LONG, 0); // already closed!
- * }
- * Additionally, access to a memory segment is subject to the thread-confinement checks enforced by the owning memory
- * session; that is, if the segment is associated with a shared session, it can be accessed by multiple threads;
- * if it is associated with a confined session, it can only be accessed by the thread which owns the memory session.
+ * Every memory segment has an {@linkplain #address() address}, expressed as a {@code long} value.
+ * The nature of a segment's address depends on the kind of the segment:
+ *
+ *
The address of a heap segment is not a physical address, but rather an offset within the region of memory
+ * which backs the segment. The region is inside the Java heap, so garbage collection might cause the region to be
+ * relocated in physical memory over time, but this is not exposed to clients of the {@code MemorySegment} API who
+ * see a stable virtualized address for a heap segment backed by the region.
+ * A heap segment obtained from one of the {@link #ofArray(int[])} factory methods has an address of zero.
+ *
The address of a native segment (including mapped segments) denotes the physical address of the region of
+ * memory which backs the segment.
+ *
+ *
+ * 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
+ * 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
+ * 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}).
+ * That is, a memory segment has temporal bounds.
*
- * Heap segments are always associated with the {@linkplain MemorySession#global() global} memory session.
- * This session cannot be closed, and segments associated with it can be considered as always alive.
- * Buffer segments are typically associated with the global memory session, with one exception: buffer segments created
- * from byte buffer instances obtained calling the {@link #asByteBuffer()} method on a memory segment {@code S}
- * are associated with the same memory session as {@code S}.
+ * 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.
*
- *
Dereferencing memory segments
+ *
Accessing memory segments
*
- * A memory segment can be read or written using various methods provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}).
- * Each dereference method takes a {@linkplain ValueLayout value layout}, which specifies the size,
- * alignment constraints, byte order as well as the Java type associated with the dereference operation, and an offset.
+ * A memory segment can be read or written using various access operations provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}).
+ * Each access operation takes a {@linkplain ValueLayout value layout}, which specifies the size and shape of the value,
+ * and an offset, expressed in bytes.
* For instance, to read an int from a segment, using {@linkplain ByteOrder#nativeOrder() default endianness}, the following code can be used:
* {@snippet lang=java :
* MemorySegment segment = ...
* int value = segment.get(ValueLayout.JAVA_INT, 0);
* }
*
- * If the value to be read is stored in memory using {@linkplain ByteOrder#BIG_ENDIAN big-endian} encoding, the dereference operation
+ * If the value to be read is stored in memory using {@linkplain ByteOrder#BIG_ENDIAN big-endian} encoding, the access operation
* can be expressed as follows:
* {@snippet lang=java :
* MemorySegment segment = ...
* int value = segment.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
* }
*
- * For more complex dereference operations (e.g. structured memory access), clients can obtain a
- * {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) memory segment view var handle},
- * that is, a var handle that accepts a segment and a {@code long} offset. More complex access var handles
+ * For more complex access operations (e.g. structured memory access), clients can obtain a
+ * {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) var handle}
+ * that accepts a segment and a {@code long} offset. More complex var handles
* can be obtained by adapting a segment var handle view using the var handle combinator functions defined in the
* {@link java.lang.invoke.MethodHandles} class:
*
@@ -126,7 +144,7 @@
* intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12
* }
*
- * Alternatively, complex access var handles can can be obtained
+ * Alternatively, complex var handles can can be obtained
* from {@linkplain MemoryLayout#varHandle(MemoryLayout.PathElement...) memory layouts}
* by providing a so called layout path:
*
@@ -138,62 +156,138 @@
*
*
Slicing memory segments
*
- * Memory segments support slicing. A memory segment can be used to {@linkplain MemorySegment#asSlice(long, long) obtain}
- * other segments backed by the same underlying memory region, but with stricter spatial bounds than the ones
- * of the original segment:
+ * Memory segments support {@linkplain MemorySegment#asSlice(long, long) slicing}. Slicing a memory segment
+ * returns a new memory segment that is backed by the same region of memory as the original. The address of the sliced
+ * 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 :
- * MemorySession session = ...
- * MemorySegment segment = MemorySegment.allocateNative(100, session);
+ * Arena arena = ...
+ * MemorySegment segment = arena.allocate(100);
* MemorySegment slice = segment.asSlice(50, 10);
* slice.get(ValueLayout.JAVA_INT, 20); // Out of bounds!
- * session.close();
+ * 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. As a result, attempting to read an int value at offset 20 of the
- * {@code slice} segment will result in an exception. The {@linkplain MemorySession temporal bounds} of the original segment
- * are inherited by its slices; that is, when the memory session associated with {@code segment} is closed, {@code slice}
- * will also be become inaccessible.
+ * 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} 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 shared memory session). The following code can be used to sum all int
- * values in a memory segment in parallel:
+ * (to do this, the segment has to be associated with a scope that allows {@linkplain SegmentScope#isAccessibleBy(Thread) access}
+ * from multiple threads). The following code can be used to sum all int values in a memory segment in parallel:
*
- * {@snippet lang=java :
- * try (MemorySession session = MemorySession.openShared()) {
+ * {@snippet lang = java:
+ * try (Arena arena = Arena.openShared()) {
* SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT);
- * MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, session);
+ * MemorySegment segment = arena.allocate(SEQUENCE_LAYOUT);
* int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
* .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
* .sum();
* }
- * }
+ *}
*
*
Alignment
*
- * When dereferencing a memory segment using a layout, the runtime must check that the segment address being dereferenced
- * matches the layout's {@linkplain MemoryLayout#byteAlignment() alignment constraints}. If the segment being
- * dereferenced is a native segment, then it has a concrete {@linkplain #address() base address}, which can
- * be used to perform the alignment check. The pseudo-function below demonstrates this:
+ * Access operations on a memory segment are constrained not only by the spatial and temporal bounds of the segment,
+ * but also by the alignment constraint of the value layout specified to the operation. An access operation can
+ * access only those offsets in the segment that denote addresses in physical memory which are aligned according
+ * to the layout. An address in physical memory is aligned according to a layout if the address is an integer
+ * multiple of the layout's alignment constraint. For example, the address 1000 is aligned according to an 8-byte alignment
+ * constraint (because 1000 is an integer multiple of 8), and to a 4-byte alignment constraint, and to a 2-byte alignment
+ * constraint; in contrast, the address 1004 is aligned according to a 4-byte alignment constraint, and to a 2-byte alignment
+ * constraint, but not to an 8-byte alignment constraint.
+ * Access operations are required to respect alignment because it can impact the performance of access operations, and
+ * can also determine which access operations are available at a given physical address. For instance,
+ * {@linkplain java.lang.invoke.VarHandle#compareAndSet(Object...) atomic access operations} operations using
+ * {@link java.lang.invoke.VarHandle} are only permitted at aligned addresses. In addition, alignment
+ * applies to an access operation whether the segment being accessed is a native segment or a heap segment.
+ *
+ * If the segment being accessed is a native segment, then its {@linkplain #address() address} in physical memory can be
+ * combined with the offset to obtain the target address in physical memory. The pseudo-function below demonstrates this:
*
- * {@snippet lang=java :
+ * {@snippet lang = java:
* boolean isAligned(MemorySegment segment, long offset, MemoryLayout layout) {
- * return ((segment.address().toRawLongValue() + offset) % layout.byteAlignment()) == 0
+ * return ((segment.address() + offset) % layout.byteAlignment()) == 0;
* }
* }
*
- * If, however, the segment being dereferenced is a heap segment, the above function will not work: a heap
- * segment's base address is virtualized and, as such, cannot be used to construct an alignment check. Instead,
- * heap segments are assumed to produce addresses which are never more aligned than the element size of the Java array from which
- * they have originated from, as shown in the following table:
+ * For example:
+ *
+ *
A native segment with address 1000 can be accessed at offsets 0, 8, 16, 24, etc under an 8-byte alignment constraint,
+ * because the target addresses (1000, 1008, 1016, 1024) are 8-byte aligned.
+ * Access at offsets 1-7 or 9-15 or 17-23 is disallowed because the target addresses would not be 8-byte aligned.
+ *
A native segment with address 1000 can be accessed at offsets 0, 4, 8, 12, etc under a 4-byte alignment constraint,
+ * because the target addresses (1000, 1004, 1008, 1012) are 4-byte aligned.
+ * Access at offsets 1-3 or 5-7 or 9-11 is disallowed because the target addresses would not be 4-byte aligned.
+ *
A native segment with address 1000 can be accessed at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint,
+ * because the target addresses (1000, 1002, 1004, 1006) are 2-byte aligned.
+ * Access at offsets 1 or 3 or 5 is disallowed because the target addresses would not be 2-byte aligned.
+ *
A native segment with address 1004 can be accessed at offsets 0, 4, 8, 12, etc under a 4-byte alignment constraint,
+ * and at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint.
+ * Under an 8-byte alignment constraint, it can be accessed at offsets 4, 12, 20, 28, etc.
+ *
A native segment with address 1006 can be accessed at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint.
+ * Under a 4-byte alignment constraint, it can be accessed at offsets 2, 6, 10, 14, etc.
+ * Under an 8-byte alignment constraint, it can be accessed at offsets 2, 10, 18, 26, etc.
+ *
A native segment with address 1007 can be accessed at offsets 0, 1, 2, 3, etc under a 1-byte alignment constraint.
+ * Under a 2-byte alignment constraint, it can be accessed at offsets 1, 3, 5, 7, etc.
+ * Under a 4-byte alignment constraint, it can be accessed at offsets 1, 5, 9, 13, etc.
+ * Under an 8-byte alignment constraint, it can be accessed at offsets 1, 9, 17, 25, etc.
+ *
+ *
+ * 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 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.
+ *
+ * If the segment being accessed is a heap segment, then determining whether access is aligned is more complex.
+ * The address of the segment in physical memory is not known, and is not even fixed (it may change when the segment
+ * is relocated during garbage collection). This means that the address cannot be combined with the specified offset to
+ * determine a target address in physical memory. Since the alignment constraint always refers to alignment of
+ * addresses in physical memory, it is not possible in principle to determine if any offset in a heap segment is aligned.
+ * For example, suppose the programmer chooses a 8-byte alignment constraint and tries
+ * to access offset 16 in a heap segment. If the heap segment's address 0 corresponds to physical address 1000,
+ * then the target address (1016) would be aligned, but if address 0 corresponds to physical address 1004,
+ * then the target address (1020) would not be aligned. It is undesirable to allow access to target addresses that are
+ * aligned according to the programmer's chosen alignment constraint, but might not be predictably aligned in physical memory
+ * (e.g. because of platform considerations and/or garbage collection behavior).
+ *
+ * In practice, the Java runtime lays out arrays in memory so that each n-byte element occurs at an n-byte
+ * aligned physical address. The runtime preserves this invariant even if the array is relocated during garbage
+ * collection. Access operations rely on this invariant to determine if the specified offset in a heap segment refers
+ * to an aligned address in physical memory. For example:
+ *
+ *
The starting physical address of a {@code long[]} array will be 8-byte aligned (e.g. 1000), so that successive long elements
+ * occur at 8-byte aligned addresses (e.g., 1000, 1008, 1016, 1024, etc.) A heap segment backed by a {@code long[]} array
+ * can be accessed at offsets 0, 8, 16, 24, etc under an 8-byte alignment constraint. In addition, the segment can be
+ * accessed at offsets 0, 4, 8, 12, etc under a 4-byte alignment constraint, because the target addresses
+ * (1000, 1004, 1008, 1012) are 4-byte aligned. And, the segment can be accessed at offsets 0, 2, 4, 6, etc under a
+ * 2-byte alignment constraint, because the target addresses (e.g. 1000, 1002, 1004, 1006) are 2-byte aligned.
+ *
The starting physical address of a {@code short[]} array will be 2-byte aligned (e.g. 1006) so that successive
+ * short elements occur at 2-byte aligned addresses (e.g. 1006, 1008, 1010, 1012, etc). A heap segment backed by a
+ * {@code short[]} array can be accessed at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint. The segment cannot
+ * be accessed at any offset under a 4-byte alignment constraint, because there is no guarantee that the target
+ * address would be 4-byte aligned, e.g., offset 0 would correspond to physical address 1006 while offset 1 would correspond
+ * to physical address 1007. Similarly, the segment cannot be accessed at any offset under an 8-byte alignment constraint,
+ * because because there is no guarantee that the target address would be 8-byte aligned, e.g., offset 2 would correspond
+ * to physical address 1008 but offset 4 would correspond to physical address 1010.
+ *
+ *
+ * In other words, heap segments feature a maximum alignment which is derived from the size of the elements of
+ * the Java array backing the segment, as shown in the following table:
*
*
- *
Array type of an array backing a segment and its address alignment
+ *
Maximum alignment of heap segments
*
*
- *
Array type
- *
Alignment
+ *
Array type (of backing region)
+ *
Maximum supported alignment (in bytes)
*
*
*
@@ -216,22 +310,86 @@
*
*
*
- * Note that the above definition is conservative: it might be possible, for instance, that a heap segment
- * constructed from a {@code byte[]} might have a subset of addresses {@code S} which happen to be 8-byte aligned. But determining
- * which segment addresses belong to {@code S} requires reasoning about details which are ultimately implementation-dependent.
+ * Heap segments can only be accessed using a layout whose alignment is smaller or equal to the
+ * maximum alignment associated with the heap segment. Attempting to access a heap segment using a layout
+ * whose alignment is greater than the maximum alignment associated with the heap segment will fail,
+ * as demonstrated in the following example:
+ *
+ * {@snippet lang=java :
+ * MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]);
+ * byteSegment.get(ValueLayout.JAVA_INT, 0); // fails: layout alignment is 4, segment max alignment is 1
+ * }
+ *
+ * In such circumstances, clients have two options. They can use a heap segment backed by a different array
+ * type (e.g. {@code long[]}), capable of supporting greater maximum alignment:
+ *
+ * {@snippet lang=java :
+ * MemorySegment longSegment = MemorySegment.ofArray(new long[10]);
+ * longSegment.get(ValueLayout.JAVA_INT, 0); // ok: layout alignment is 4, segment max alignment is 8
+ * }
+ *
+ * Alternatively, they can invoke the access operation with an unaligned layout.
+ * All unaligned layout constants (e.g. {@link ValueLayout#JAVA_INT_UNALIGNED}) have their alignment constraint set to 1:
+ * {@snippet lang=java :
+ * MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]);
+ * byteSegment.get(ValueLayout.JAVA_INT_UNALIGNED, 0); // ok: layout alignment is 1, segment max alignment is 1
+ * }
+ *
+ *
Zero-length memory segments
+ *
+ * When interacting with foreign functions, it is common for those functions
+ * to allocate a region of memory and return a pointer to that region. Modeling the region of memory with a memory segment
+ * is challenging because the Java runtime has no insight into the size of the region. Only the address of the start of
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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:
+ *
+ * {@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
+ *}
+ *
+ * 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:
+ *
+ * {@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
+ *}
*
- *
Restricted memory segments
- * Sometimes it is necessary to turn a memory address obtained from native code into a memory segment with
- * full spatial, temporal and confinement bounds. To do this, clients can {@linkplain #ofAddress(MemoryAddress, long, MemorySession) obtain}
- * a native segment unsafely from a give memory address, by providing the segment size, as well as the segment {@linkplain MemorySession session}.
- * This is a restricted operation and should be used with
- * caution: for instance, an incorrect segment size could result in a VM crash when attempting to dereference
- * the memory segment.
+ * Both {@link #ofAddress(long, long, SegmentScope)} and {@link ValueLayout.OfAddress#asUnbounded()} 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.
*
- * Clients requiring sophisticated, low-level control over mapped memory segments, might consider writing
- * custom mapped memory segment factories; using {@link Linker}, e.g. on Linux, it is possible to call {@code mmap}
- * with the desired parameters; the returned address can be easily wrapped into a memory segment, using
- * {@link MemoryAddress#ofLong(long)} and {@link MemorySegment#ofAddress(MemoryAddress, long, MemorySession)}.
+ * 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.
*
* @implSpec
* Implementations of this interface are immutable, thread-safe and value-based.
@@ -239,18 +397,17 @@
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public sealed interface MemorySegment extends Addressable permits AbstractMemorySegmentImpl {
+public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
/**
- * {@return the base memory address associated with this native memory segment}
- * @throws UnsupportedOperationException if this segment is not a {@linkplain #isNative() native} segment.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * {@return the address of this memory segment}
*/
- @Override
- MemoryAddress address();
+ long address();
+
+ /**
+ * {@return the Java array associated with this memory segment, if any}
+ */
+ Optional
*
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
@@ -580,7 +742,7 @@ default MemorySegment copyFrom(MemorySegment src) {
* Wraps this segment in a {@link ByteBuffer}. Some properties of the returned buffer are linked to
* the properties of this segment. For instance, if this segment is immutable
* (e.g. the segment is a read-only segment, see {@link #isReadOnly()}), then the resulting buffer is read-only
- * (see {@link ByteBuffer#isReadOnly()}). Additionally, if this is a native memory segment, the resulting buffer is
+ * (see {@link ByteBuffer#isReadOnly()}). Additionally, if this is a native segment, the resulting buffer is
* direct (see {@link ByteBuffer#isDirect()}).
*
* The returned buffer's position (see {@link ByteBuffer#position()}) is initially set to zero, while
@@ -589,13 +751,12 @@ default MemorySegment copyFrom(MemorySegment src) {
* 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 memory session associated with this segment has been closed (see {@link MemorySession#close()}), will
- * throw an {@link IllegalStateException}. Similarly, accessing the returned buffer from a thread other than
- * the thread {@linkplain MemorySession#ownerThread() owning} this segment's memory session will throw
- * a {@link WrongThreadException}.
+ * after the scope associated with this segment is no longer {@linkplain SegmentScope#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}.
*
- * If this segment is associated with a confined memory session, calling read/write I/O operations on the resulting buffer
- * might result in an unspecified exception being thrown. Examples of such problematic operations are
+ * If this segment is associated with a scope that can only be accessed 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)}.
*
@@ -614,10 +775,10 @@ default MemorySegment copyFrom(MemorySegment src) {
* @param elementLayout the source element layout. If the byte order associated with the layout is
* 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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 this segment's contents cannot be copied into a {@code byte[]} instance,
* e.g. its size is greater than {@link Integer#MAX_VALUE}.
*/
@@ -628,10 +789,10 @@ default MemorySegment copyFrom(MemorySegment src) {
* @param elementLayout the source element layout. If the byte order associated with the layout is
* 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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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}
*/
@@ -642,10 +803,10 @@ default MemorySegment copyFrom(MemorySegment src) {
* @param elementLayout the source element layout. If the byte order associated with the layout is
* 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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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}.
*/
@@ -656,10 +817,10 @@ default MemorySegment copyFrom(MemorySegment src) {
* @param elementLayout the source element layout. If the byte order associated with the layout is
* 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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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}.
*/
@@ -670,10 +831,10 @@ default MemorySegment copyFrom(MemorySegment src) {
* @param elementLayout the source element layout. If the byte order associated with the layout is
* 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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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}.
*/
@@ -684,10 +845,10 @@ default MemorySegment copyFrom(MemorySegment src) {
* @param elementLayout the source element layout. If the byte order associated with the layout is
* 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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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}.
*/
@@ -698,10 +859,10 @@ default MemorySegment copyFrom(MemorySegment src) {
* @param elementLayout the source element layout. If the byte order associated with the layout is
* 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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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}.
*/
@@ -714,17 +875,16 @@ default MemorySegment copyFrom(MemorySegment src) {
* sequences with this charset's default replacement string. The {@link
* java.nio.charset.CharsetDecoder} class should be used when more control
* over the decoding process is required.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param offset offset in bytes (relative to this segment address) at which this access operation will occur.
* @return a Java string constructed from the bytes read from the given starting address up to (but not including)
* the first {@code '\0'} terminator character (assuming one is found).
* @throws IllegalArgumentException if the size of the UTF-8 string is greater than the largest string supported by the platform.
- * @throws IndexOutOfBoundsException if {@code S + offset > byteSize()}, where {@code S} is the size of the UTF-8
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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}.
*/
default String getUtf8String(long offset) {
return SharedUtils.toJavaStringInternal(this, offset);
@@ -742,15 +902,14 @@ default String getUtf8String(long offset) {
* copied as well. This means that, depending on the method used to read
* the string, such as {@link MemorySegment#getUtf8String(long)}, the string
* will appear truncated when read again.
- *
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param offset offset in bytes (relative to this segment address) at which this access operation will occur.
+ * the final address of this write operation can be expressed as {@code address() + offset}.
* @param str the Java string to be written into this segment.
- * @throws IndexOutOfBoundsException if {@code str.getBytes().length() + offset >= byteSize()}.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with 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}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code scope().isAccessibleBy(T) == false}.
*/
default void setUtf8String(long offset, String str) {
Utils.toCString(str.getBytes(StandardCharsets.UTF_8), SegmentAllocator.prefixAllocator(asSlice(offset)));
@@ -758,112 +917,212 @@ default void setUtf8String(long offset, String str) {
/**
- * Creates a buffer memory segment that models the memory associated with the given {@link Buffer} instance.
+ * Creates a memory segment that is backed by the same region of memory that backs the given {@link Buffer} instance.
* The segment starts relative to the buffer's position (inclusive) and ends relative to the buffer's limit (exclusive).
*
- * If the buffer is {@linkplain ByteBuffer#isReadOnly() read-only}, the resulting segment will also be
- * {@linkplain ByteBuffer#isReadOnly() read-only}. The memory session associated with this segment can either be the
- * {@linkplain MemorySession#global() global} memory session, in case the buffer has been created independently,
- * or some other memory session, in case the buffer has been obtained using {@link #asByteBuffer()}.
+ * If the buffer is {@linkplain Buffer#isReadOnly() read-only}, the resulting segment will also be
+ * {@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 resulting memory segment keeps a reference to the backing buffer, keeping it reachable.
+ * 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.
+ *
*
- * @param buffer the buffer instance backing the buffer memory segment.
- * @return a buffer memory segment.
+ * @param buffer the buffer instance to be turned into a new memory segment.
+ * @return a memory segment, derived from the given buffer instance.
+ * @throws IllegalArgumentException if the provided {@code buffer} is a heap buffer but is not backed by an array.
+ * For example, buffers directly or indirectly obtained via
+ * ({@link CharBuffer#wrap(CharSequence)} or {@link CharBuffer#wrap(char[], int, int)}
+ * are not backed by an array.
*/
static MemorySegment ofBuffer(Buffer buffer) {
return AbstractMemorySegmentImpl.ofBuffer(buffer);
}
/**
- * Creates an array memory segment that models the memory associated with the given heap-allocated byte array.
- * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session.
+ * 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.
*
- * @param arr the primitive array backing the array memory segment.
- * @return an array memory segment.
+ * @param byteArray the primitive array backing the heap memory segment.
+ * @return a heap memory segment backed by a byte array.
*/
- static MemorySegment ofArray(byte[] arr) {
- return HeapMemorySegmentImpl.OfByte.fromArray(arr);
+ static MemorySegment ofArray(byte[] byteArray) {
+ return HeapMemorySegmentImpl.OfByte.fromArray(byteArray);
}
/**
- * Creates an array memory segment that models the memory associated with the given heap-allocated char array.
- * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session.
+ * 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.
*
- * @param arr the primitive array backing the array memory segment.
- * @return an array memory segment.
+ * @param charArray the primitive array backing the heap segment.
+ * @return a heap memory segment backed by a char array.
*/
- static MemorySegment ofArray(char[] arr) {
- return HeapMemorySegmentImpl.OfChar.fromArray(arr);
+ static MemorySegment ofArray(char[] charArray) {
+ return HeapMemorySegmentImpl.OfChar.fromArray(charArray);
}
/**
- * Creates an array memory segment that models the memory associated with the given heap-allocated short array.
- * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session.
+ * 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.
*
- * @param arr the primitive array backing the array memory segment.
- * @return an array memory segment.
+ * @param shortArray the primitive array backing the heap segment.
+ * @return a heap memory segment backed by a short array.
*/
- static MemorySegment ofArray(short[] arr) {
- return HeapMemorySegmentImpl.OfShort.fromArray(arr);
+ static MemorySegment ofArray(short[] shortArray) {
+ return HeapMemorySegmentImpl.OfShort.fromArray(shortArray);
}
/**
- * Creates an array memory segment that models the memory associated with the given heap-allocated int array.
- * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session.
+ * 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.
*
- * @param arr the primitive array backing the array memory segment.
- * @return an array memory segment.
+ * @param intArray the primitive array backing the heap segment.
+ * @return a heap memory segment backed by an int array.
*/
- static MemorySegment ofArray(int[] arr) {
- return HeapMemorySegmentImpl.OfInt.fromArray(arr);
+ static MemorySegment ofArray(int[] intArray) {
+ return HeapMemorySegmentImpl.OfInt.fromArray(intArray);
}
/**
- * Creates an array memory segment that models the memory associated with the given heap-allocated float array.
- * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session.
+ * 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.
*
- * @param arr the primitive array backing the array memory segment.
- * @return an array memory segment.
+ * @param floatArray the primitive array backing the heap segment.
+ * @return a heap memory segment backed by a float array.
*/
- static MemorySegment ofArray(float[] arr) {
- return HeapMemorySegmentImpl.OfFloat.fromArray(arr);
+ static MemorySegment ofArray(float[] floatArray) {
+ return HeapMemorySegmentImpl.OfFloat.fromArray(floatArray);
}
/**
- * Creates an array memory segment that models the memory associated with the given heap-allocated long array.
- * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session.
+ * 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.
*
- * @param arr the primitive array backing the array memory segment.
- * @return an array memory segment.
+ * @param longArray the primitive array backing the heap segment.
+ * @return a heap memory segment backed by a long array.
*/
- static MemorySegment ofArray(long[] arr) {
- return HeapMemorySegmentImpl.OfLong.fromArray(arr);
+ static MemorySegment ofArray(long[] longArray) {
+ return HeapMemorySegmentImpl.OfLong.fromArray(longArray);
}
/**
- * Creates an array memory segment that models the memory associated with the given heap-allocated double array.
- * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session.
+ * 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.
*
- * @param arr the primitive array backing the array memory segment.
- * @return an array memory segment.
+ * @param doubleArray the primitive array backing the heap segment.
+ * @return a heap memory segment backed by a double array.
*/
- static MemorySegment ofArray(double[] arr) {
- return HeapMemorySegmentImpl.OfDouble.fromArray(arr);
+ static MemorySegment ofArray(double[] doubleArray) {
+ return HeapMemorySegmentImpl.OfDouble.fromArray(doubleArray);
}
+ /**
+ * A zero-length native segment modelling the {@code NULL} address.
+ */
+ MemorySegment NULL = NativeMemorySegmentImpl.makeNativeSegmentUnchecked(0L, 0);
/**
- * Creates a native memory segment with the given size, base address, and memory session.
+ * 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}.
+ *
+ * This is equivalent to the following code:
+ * {@snippet lang = java:
+ * ofAddress(address, 0);
+ *}
+ * @param address the address of the returned native segment.
+ * @return a zero-length native segment with the given address.
+ */
+ static MemorySegment ofAddress(long address) {
+ 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 access to this method occurs from a module {@code M} and the command line option
+ * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
+ * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
+ */
+ @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 memory region is typically obtained from foreign code
+ * 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 memory session.
+ * provided scope.
+ *
+ * This is equivalent to the following code:
+ * {@snippet lang = java:
+ * ofAddress(address, byteSize, scope, null);
+ *}
+ *
+ * @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 access to this method occurs from a module {@code M} and the command line option
+ * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
+ * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
+ */
+ @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 memory location from Java code will either return an arbitrary value,
+ * 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.
@@ -872,97 +1131,127 @@ static MemorySegment ofArray(double[] arr) {
* restricted methods, and use safe and supported functionalities, where possible.
*
*
- * @param address the returned segment's base address.
- * @param bytesSize the desired size.
- * @param session the native segment memory session.
- * @return a native memory segment with the given base address, size and memory session.
- * @throws IllegalArgumentException if {@code bytesSize < 0}.
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
+ * @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 access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
- static MemorySegment ofAddress(MemoryAddress address, long bytesSize, MemorySession session) {
+ static MemorySegment ofAddress(long address, long byteSize, SegmentScope scope, Runnable cleanupAction) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemorySegment.class, "ofAddress");
- Objects.requireNonNull(address);
- Objects.requireNonNull(session);
- Utils.checkAllocationSizeAndAlign(bytesSize, 1);
- return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, bytesSize, session);
+ Objects.requireNonNull(scope);
+ Utils.checkAllocationSizeAndAlign(byteSize, 1);
+ return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, byteSize, scope, cleanupAction);
}
/**
- * Creates a native memory segment with the given layout and memory session.
- * A client is responsible for ensuring that the memory session associated with the returned segment is closed
- * when the segment is no longer in use. Failure to do so will result in off-heap memory leaks.
+ * 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(), session);
+ * allocateNative(layout.bytesSize(), layout.bytesAlignment(), scope);
* }
*
- * The block of off-heap memory associated with the returned native memory segment is initialized to zero.
+ * The region of off-heap region backing the returned native segment is initialized to zero.
*
- * @param layout the layout of the off-heap memory block backing the native memory segment.
- * @param session the segment memory session.
- * @return a new native memory segment.
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
+ * @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, MemorySession session) {
- Objects.requireNonNull(session);
+ static MemorySegment allocateNative(MemoryLayout layout, SegmentScope scope) {
Objects.requireNonNull(layout);
- return allocateNative(layout.byteSize(), layout.byteAlignment(), session);
+ Objects.requireNonNull(scope);
+ return allocateNative(layout.byteSize(), layout.byteAlignment(), scope);
}
/**
- * Creates a native memory segment with the given size (in bytes) and memory session.
- * A client is responsible for ensuring that the memory session associated with the returned segment is closed
- * when the segment is no longer in use. Failure to do so will result in off-heap memory leaks.
+ * 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, session);
+ * allocateNative(bytesSize, 1, scope);
* }
*
- * The block of off-heap memory associated with the returned native memory segment is initialized to zero.
+ * The region of off-heap region backing the returned native segment is initialized to zero.
*
- * @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
- * @param session the segment temporal bounds.
+ * @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 bytesSize < 0}.
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
+ * @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 bytesSize, MemorySession session) {
- return allocateNative(bytesSize, 1, session);
+ static MemorySegment allocateNative(long byteSize, SegmentScope scope) {
+ return allocateNative(byteSize, 1, scope);
}
/**
- * Creates a native memory segment with the given size (in bytes), alignment constraint (in bytes) and memory session.
- * A client is responsible for ensuring that the memory session associated with the returned segment is closed when the
- * segment is no longer in use. Failure to do so will result in off-heap memory leaks.
+ * 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 block of off-heap memory associated with the returned native memory segment is initialized to zero.
+ * 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 bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
- * @param alignmentBytes the alignment constraint (in bytes) of the off-heap memory block backing the native memory segment.
- * @param session the segment memory session.
+ * @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 bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes}
- * is not a power of 2.
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
- */
- static MemorySegment allocateNative(long bytesSize, long alignmentBytes, MemorySession session) {
- Objects.requireNonNull(session);
- Utils.checkAllocationSizeAndAlign(bytesSize, alignmentBytes);
- return NativeMemorySegmentImpl.makeNativeSegment(bytesSize, alignmentBytes, session);
+ * @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);
}
/**
@@ -988,14 +1277,14 @@ static MemorySegment allocateNative(long bytesSize, long alignmentBytes, MemoryS
* @param dstSegment the destination segment.
* @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 #session() session} associated with {@code srcSegment} is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with {@code srcSegment}.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code dstSegment} is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with {@code dstSegment}.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not
+ * {@linkplain SegmentScope#isAlive() alive}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code srcSegment.scope().isAccessibleBy(T) == false}.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not
+ * {@linkplain SegmentScope#isAlive() alive}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code dstSegment.scope().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}.
@@ -1033,16 +1322,16 @@ static void copy(MemorySegment srcSegment, long srcOffset, MemorySegment dstSegm
* @param dstOffset the starting offset, in bytes, of the destination segment.
* @param elementCount the number of elements to be copied.
* @throws IllegalArgumentException if the element layouts have different sizes, if the source (resp. destination) segment/offset are
- * incompatible with the alignment constraints in the source
+ * 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 #session() session} associated with {@code srcSegment} is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this {@code srcSegment}.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code dstSegment} is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with {@code dstSegment}.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not
+ * {@linkplain SegmentScope#isAlive() alive}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code srcSegment().scope().isAccessibleBy(T) == false}.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not
+ * {@linkplain SegmentScope#isAlive() alive}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code dstSegment().scope().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}.
@@ -1085,429 +1374,414 @@ static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long sr
/**
* Reads a byte from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return a byte value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 byte value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default byte get(ValueLayout.OfByte layout, long offset) {
- return (byte)layout.accessHandle().get(this, offset);
+ return (byte) ((ValueLayouts.OfByteImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes a byte into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfByte layout, long offset, byte value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfByteImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads a boolean from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return a boolean value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 boolean value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default boolean get(ValueLayout.OfBoolean layout, long offset) {
- return (boolean)layout.accessHandle().get(this, offset);
+ return (boolean) ((ValueLayouts.OfBooleanImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes a boolean into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfBoolean layout, long offset, boolean value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfBooleanImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads a char from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return a char value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 char value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default char get(ValueLayout.OfChar layout, long offset) {
- return (char)layout.accessHandle().get(this, offset);
+ return (char) ((ValueLayouts.OfCharImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes a char into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfChar layout, long offset, char value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfCharImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads a short from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return a short value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 short value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default short get(ValueLayout.OfShort layout, long offset) {
- return (short)layout.accessHandle().get(this, offset);
+ return (short) ((ValueLayouts.OfShortImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes a short into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfShort layout, long offset, short value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfShortImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads an int from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return an int value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 an int value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default int get(ValueLayout.OfInt layout, long offset) {
- return (int)layout.accessHandle().get(this, offset);
+ return (int) ((ValueLayouts.OfIntImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes an int into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfInt layout, long offset, int value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfIntImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads a float from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return a float value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 float value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default float get(ValueLayout.OfFloat layout, long offset) {
- return (float)layout.accessHandle().get(this, offset);
+ return (float)((ValueLayouts.OfFloatImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes a float into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfFloat layout, long offset, float value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfFloatImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads a long from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return a long value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 long value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default long get(ValueLayout.OfLong layout, long offset) {
- return (long)layout.accessHandle().get(this, offset);
+ return (long) ((ValueLayouts.OfLongImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes a long into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfLong layout, long offset, long value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfLongImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads a double from this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return a double value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @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 double value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
*/
@ForceInline
default double get(ValueLayout.OfDouble layout, long offset) {
- return (double)layout.accessHandle().get(this, offset);
+ return (double) ((ValueLayouts.OfDoubleImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes a double into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
default void set(ValueLayout.OfDouble layout, long offset, double value) {
- layout.accessHandle().set(this, offset, value);
+ ((ValueLayouts.OfDoubleImpl) layout).accessHandle().set(this, offset, value);
}
/**
- * Reads an address from this segment at the given offset, with the given layout.
- *
- * @param layout the layout of the memory region to be read.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + offset}.
- * @return an address value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * 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}.
+ * @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}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code scope().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
* memory segment.
*/
@ForceInline
- default MemoryAddress get(ValueLayout.OfAddress layout, long offset) {
- return (MemoryAddress)layout.accessHandle().get(this, offset);
+ default MemorySegment get(ValueLayout.OfAddress layout, long offset) {
+ return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, offset);
}
/**
* Writes an address into this segment at the given offset, with the given layout.
*
- * @param layout the layout of the memory region to be written.
- * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + offset}.
+ * @param layout the layout of the region of memory to be written.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout.
- * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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
* memory segment.
* @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}.
*/
@ForceInline
- default void set(ValueLayout.OfAddress layout, long offset, Addressable value) {
- layout.accessHandle().set(this, offset, value.address());
+ default void set(ValueLayout.OfAddress layout, long offset, MemorySegment value) {
+ ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, offset, value);
}
/**
* Reads a char from this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be read.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
- * @return a char value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @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 char value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the
* memory segment.
*/
@ForceInline
default char getAtIndex(ValueLayout.OfChar 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 (char)layout.accessHandle().get(this, index * layout.byteSize());
+ return (char) ((ValueLayouts.OfCharImpl) layout).accessHandle().get(this, index * layout.byteSize());
}
/**
* Writes a char into this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be written.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
+ * @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 char value to be written.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @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}.
*/
@@ -1515,48 +1789,48 @@ default char getAtIndex(ValueLayout.OfChar layout, long index) {
default void setAtIndex(ValueLayout.OfChar layout, long index, char value) {
Utils.checkElementAlignment(layout, "Layout alignment greater than its size");
// note: we know size is a small value (as it comes from ValueLayout::byteSize())
- layout.accessHandle().set(this, index * layout.byteSize(), value);
+ ((ValueLayouts.OfCharImpl) layout).accessHandle().set(this, index * layout.byteSize(), value);
}
/**
* Reads a short from this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be read.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
- * @return a short value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @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 short value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the
* memory segment.
*/
@ForceInline
default short getAtIndex(ValueLayout.OfShort 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 (short)layout.accessHandle().get(this, index * layout.byteSize());
+ return (short) ((ValueLayouts.OfShortImpl) layout).accessHandle().get(this, index * layout.byteSize());
}
/**
* Writes a short into this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be written.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
+ * @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 #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @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}.
*/
@@ -1564,48 +1838,48 @@ default short getAtIndex(ValueLayout.OfShort layout, long index) {
default void setAtIndex(ValueLayout.OfShort layout, long index, short value) {
Utils.checkElementAlignment(layout, "Layout alignment greater than its size");
// note: we know size is a small value (as it comes from ValueLayout::byteSize())
- layout.accessHandle().set(this, index * layout.byteSize(), value);
+ ((ValueLayouts.OfShortImpl) layout).accessHandle().set(this, index * layout.byteSize(), value);
}
/**
* Reads an int from this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be read.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
- * @return an int value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @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 an int value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the
* memory segment.
*/
@ForceInline
default int getAtIndex(ValueLayout.OfInt 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 (int)layout.accessHandle().get(this, index * layout.byteSize());
+ return (int) ((ValueLayouts.OfIntImpl) layout).accessHandle().get(this, index * layout.byteSize());
}
/**
* Writes an int into this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be written.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
+ * @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 int value to be written.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @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}.
*/
@@ -1613,48 +1887,48 @@ default int getAtIndex(ValueLayout.OfInt layout, long index) {
default void setAtIndex(ValueLayout.OfInt layout, long index, int value) {
Utils.checkElementAlignment(layout, "Layout alignment greater than its size");
// note: we know size is a small value (as it comes from ValueLayout::byteSize())
- layout.accessHandle().set(this, index * layout.byteSize(), value);
+ ((ValueLayouts.OfIntImpl) layout).accessHandle().set(this, index * layout.byteSize(), value);
}
/**
* Reads a float from this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be read.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
- * @return a float value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @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 float value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the
* memory segment.
*/
@ForceInline
default float getAtIndex(ValueLayout.OfFloat 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 (float)layout.accessHandle().get(this, index * layout.byteSize());
+ return (float) ((ValueLayouts.OfFloatImpl) layout).accessHandle().get(this, index * layout.byteSize());
}
/**
* Writes a float into this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be written.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
+ * @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 float value to be written.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @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}.
*/
@@ -1662,48 +1936,48 @@ default float getAtIndex(ValueLayout.OfFloat layout, long index) {
default void setAtIndex(ValueLayout.OfFloat layout, long index, float value) {
Utils.checkElementAlignment(layout, "Layout alignment greater than its size");
// note: we know size is a small value (as it comes from ValueLayout::byteSize())
- layout.accessHandle().set(this, index * layout.byteSize(), value);
+ ((ValueLayouts.OfFloatImpl) layout).accessHandle().set(this, index * layout.byteSize(), value);
}
/**
* Reads a long from this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be read.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
- * @return a long value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @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 long value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the
* memory segment.
*/
@ForceInline
default long getAtIndex(ValueLayout.OfLong 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 (long)layout.accessHandle().get(this, index * layout.byteSize());
+ return (long) ((ValueLayouts.OfLongImpl) layout).accessHandle().get(this, index * layout.byteSize());
}
/**
* Writes a long into this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be written.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
+ * @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 long value to be written.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @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}.
*/
@@ -1711,48 +1985,48 @@ default long getAtIndex(ValueLayout.OfLong layout, long index) {
default void setAtIndex(ValueLayout.OfLong layout, long index, long value) {
Utils.checkElementAlignment(layout, "Layout alignment greater than its size");
// note: we know size is a small value (as it comes from ValueLayout::byteSize())
- layout.accessHandle().set(this, index * layout.byteSize(), value);
+ ((ValueLayouts.OfLongImpl) layout).accessHandle().set(this, index * layout.byteSize(), value);
}
/**
* Reads a double from this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be read.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
- * @return a double value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @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 double value read from this segment.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the
* memory segment.
*/
@ForceInline
default double getAtIndex(ValueLayout.OfDouble 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 (double)layout.accessHandle().get(this, index * layout.byteSize());
+ return (double) ((ValueLayouts.OfDoubleImpl) layout).accessHandle().get(this, index * layout.byteSize());
}
/**
* Writes a double into this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be written.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
+ * @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 double value to be written.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @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}.
*/
@@ -1760,78 +2034,82 @@ default double getAtIndex(ValueLayout.OfDouble layout, long index) {
default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {
Utils.checkElementAlignment(layout, "Layout alignment greater than its size");
// note: we know size is a small value (as it comes from ValueLayout::byteSize())
- layout.accessHandle().set(this, index * layout.byteSize(), value);
+ ((ValueLayouts.OfDoubleImpl) layout).accessHandle().set(this, index * layout.byteSize(), value);
}
/**
- * Reads an address from this segment at the given index, scaled by the given layout size.
- *
- * @param layout the layout of the memory region to be read.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this read operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
- * @return an address value read from this address.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * 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}.
+ *
+ * @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}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code scope().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 dereference operation falls outside the spatial bounds of the
+ * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the
* memory segment.
*/
@ForceInline
- default MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) {
+ default MemorySegment getAtIndex(ValueLayout.OfAddress 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 (MemoryAddress)layout.accessHandle().get(this, index * layout.byteSize());
+ return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, index * layout.byteSize());
}
/**
* Writes an address into this segment at the given index, scaled by the given layout size.
*
- * @param layout the layout of the memory region to be written.
- * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment,
- * the final address of this write operation can be expressed as {@code address().toRawLongValue() + (index * layout.byteSize())}.
+ * @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 address value to be written.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this segment.
- * @throws IllegalArgumentException if the dereference operation is
- * incompatible with the alignment constraints in the provided layout,
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment 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 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 dereference operation falls outside the spatial bounds of the
+ * @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.OfAddress layout, long index, Addressable value) {
+ default void setAtIndex(ValueLayout.OfAddress 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())
- layout.accessHandle().set(this, index * layout.byteSize(), value.address());
+ ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, index * layout.byteSize(), value);
}
/**
* Compares the specified object with this memory segment for equality. Returns {@code true} if and only if the specified
- * object is also a memory segment, and if that segment refers to the same memory region as this segment. More specifically,
- * for two segments to be considered equals, all the following must be true:
+ * object is also a memory segment, and if the two segments refer to the same location, in some region of memory.
+ * More specifically, for two segments {@code s1} and {@code s2} to be considered equals, all the following must be true:
*
- *
the two segments must be of the same kind; either both are {@linkplain #isNative() native segments},
- * backed by off-heap memory, or both are backed by on-heap memory;
- *
if the two segments are {@linkplain #isNative() native segments}, their {@link #address() base address}
- * must be {@linkplain MemoryAddress#equals(Object) equal}. Otherwise, the two segments must wrap the
- * same Java array instance, at the same starting offset;
- *
the two segments must have the same {@linkplain #byteSize() size}; and
- *
the two segments must have the {@linkplain MemorySession#equals(Object) same} {@linkplain #session() temporal bounds}.
+ *
{@code s1.array().equals(s2.array())}, that is, the two segments must be of the same kind;
+ * either both are {@linkplain #isNative() native segments}, backed by off-heap memory, or both are backed by
+ * the same on-heap Java array;
+ *
{@code s1.address() == s2.address()}, that is, the address of the two segments should be the same.
+ * This means that the two segments either refer to the same location in some off-heap region, or they refer
+ * to the same position inside their associated Java array instance.
*
* @apiNote This method does not perform a structural comparison of the contents of the two memory segments. Clients can
- * compare memory segments structurally by using the {@link #mismatch(MemorySegment)} method instead.
+ * compare memory segments structurally by using the {@link #mismatch(MemorySegment)} method instead. Note that this
+ * method does not compare the temporal and spatial bounds of two segments. As such it is suitable
+ * to perform address checks, such as checking if a native segment has the {@code NULL} address.
*
* @param that the object to be compared for equality with this memory segment.
* @return {@code true} if the specified object is equal to this memory segment.
* @see #mismatch(MemorySegment)
- * @see #asOverlappingSlice(MemorySegment)
*/
@Override
boolean equals(Object that);
@@ -1855,13 +2133,13 @@ default void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable va
* @param dstArray the destination array.
* @param dstIndex the starting index of the destination array.
* @param elementCount the number of array elements to be copied.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code srcSegment} is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with {@code srcSegment}.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not
+ * {@linkplain SegmentScope#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,
* if the destination array component type does not match the carrier of the source element layout, if the source
- * segment/offset are incompatible with the alignment constraints in the source element layout,
+ * segment/offset are incompatible with the alignment constraint in the source element layout,
* or if the destination element layout alignment is greater than its size.
*/
@ForceInline
@@ -1871,28 +2149,10 @@ static void copy(
Objects.requireNonNull(srcSegment);
Objects.requireNonNull(dstArray);
Objects.requireNonNull(srcLayout);
- long baseAndScale = getBaseAndScale(dstArray.getClass());
- if (dstArray.getClass().componentType() != srcLayout.carrier()) {
- throw new IllegalArgumentException("Incompatible value layout: " + srcLayout);
- }
- int dstBase = (int)baseAndScale;
- int dstWidth = (int)(baseAndScale >> 32);
- AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment;
- Utils.checkElementAlignment(srcLayout, "Source layout alignment greater than its size");
- if (!srcImpl.isAlignedForElement(srcOffset, srcLayout)) {
- throw new IllegalArgumentException("Source segment incompatible with alignment constraints");
- }
- srcImpl.checkAccess(srcOffset, elementCount * dstWidth, true);
- Objects.checkFromIndexSize(dstIndex, elementCount, Array.getLength(dstArray));
- if (dstWidth == 1 || srcLayout.order() == ByteOrder.nativeOrder()) {
- ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(srcImpl.sessionImpl(), null,
- srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset,
- dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth);
- } else {
- ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.sessionImpl(), null,
- srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset,
- dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth, dstWidth);
- }
+
+ AbstractMemorySegmentImpl.copy(srcSegment, srcLayout, srcOffset,
+ dstArray, dstIndex,
+ elementCount);
}
/**
@@ -1907,13 +2167,13 @@ static void copy(
* different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element.
* @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 #session() session} associated with {@code dstSegment} is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with {@code dstSegment}.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not
+ * {@linkplain SegmentScope#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,
* if the source array component type does not match the carrier of the destination element layout, if the destination
- * segment/offset are incompatible with the alignment constraints in the destination element layout,
+ * segment/offset are incompatible with the alignment constraint in the destination element layout,
* or if the destination element layout alignment is greater than its size.
*/
@ForceInline
@@ -1923,47 +2183,52 @@ static void copy(
Objects.requireNonNull(srcArray);
Objects.requireNonNull(dstSegment);
Objects.requireNonNull(dstLayout);
- long baseAndScale = getBaseAndScale(srcArray.getClass());
- if (srcArray.getClass().componentType() != dstLayout.carrier()) {
- throw new IllegalArgumentException("Incompatible value layout: " + dstLayout);
- }
- int srcBase = (int)baseAndScale;
- int srcWidth = (int)(baseAndScale >> 32);
- Objects.checkFromIndexSize(srcIndex, elementCount, Array.getLength(srcArray));
- AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dstSegment;
- Utils.checkElementAlignment(dstLayout, "Destination layout alignment greater than its size");
- if (!destImpl.isAlignedForElement(dstOffset, dstLayout)) {
- throw new IllegalArgumentException("Destination segment incompatible with alignment constraints");
- }
- destImpl.checkAccess(dstOffset, elementCount * srcWidth, false);
- if (srcWidth == 1 || dstLayout.order() == ByteOrder.nativeOrder()) {
- ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(null, destImpl.sessionImpl(),
- srcArray, srcBase + (srcIndex * srcWidth),
- destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth);
- } else {
- ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(null, destImpl.sessionImpl(),
- srcArray, srcBase + (srcIndex * srcWidth),
- destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth, srcWidth);
- }
+
+ AbstractMemorySegmentImpl.copy(srcArray, srcIndex,
+ dstSegment, dstLayout, dstOffset,
+ elementCount);
}
- private static long getBaseAndScale(Class> arrayType) {
- if (arrayType.equals(byte[].class)) {
- return (long)Unsafe.ARRAY_BYTE_BASE_OFFSET | ((long)Unsafe.ARRAY_BYTE_INDEX_SCALE << 32);
- } else if (arrayType.equals(char[].class)) {
- return (long)Unsafe.ARRAY_CHAR_BASE_OFFSET | ((long)Unsafe.ARRAY_CHAR_INDEX_SCALE << 32);
- } else if (arrayType.equals(short[].class)) {
- return (long)Unsafe.ARRAY_SHORT_BASE_OFFSET | ((long)Unsafe.ARRAY_SHORT_INDEX_SCALE << 32);
- } else if (arrayType.equals(int[].class)) {
- return (long)Unsafe.ARRAY_INT_BASE_OFFSET | ((long) Unsafe.ARRAY_INT_INDEX_SCALE << 32);
- } else if (arrayType.equals(float[].class)) {
- return (long)Unsafe.ARRAY_FLOAT_BASE_OFFSET | ((long)Unsafe.ARRAY_FLOAT_INDEX_SCALE << 32);
- } else if (arrayType.equals(long[].class)) {
- return (long)Unsafe.ARRAY_LONG_BASE_OFFSET | ((long)Unsafe.ARRAY_LONG_INDEX_SCALE << 32);
- } else if (arrayType.equals(double[].class)) {
- return (long)Unsafe.ARRAY_DOUBLE_BASE_OFFSET | ((long)Unsafe.ARRAY_DOUBLE_INDEX_SCALE << 32);
- } else {
- throw new IllegalArgumentException("Not a supported array class: " + arrayType.getSimpleName());
- }
+ /**
+ * Finds and returns the relative offset, in bytes, of the first mismatch between the source and the destination
+ * segments. More specifically, the bytes at offset {@code srcFromOffset} through {@code srcToOffset - 1} in the
+ * source segment are compared against the bytes at offset {@code dstFromOffset} through {@code dstToOffset - 1}
+ * in the destination segment.
+ *
+ * If the two segments, over the specified ranges, share a common prefix then the returned offset is the length
+ * of the common prefix, and it follows that there is a mismatch between the two segments at that relative offset
+ * within the respective segments. If one segment is a proper prefix of the other, over the specified ranges,
+ * then the returned offset is the smallest range, and it follows that the relative offset is only
+ * valid for the segment with the larger range. Otherwise, there is no mismatch and {@code -1} is returned.
+ *
+ * @param srcSegment the source segment.
+ * @param srcFromOffset the offset (inclusive) of the first byte in the source segment to be tested.
+ * @param srcToOffset the offset (exclusive) of the last byte in the source segment to be tested.
+ * @param dstSegment the destination segment.
+ * @param dstFromOffset the offset (inclusive) of the first byte in the destination segment to be tested.
+ * @param dstToOffset the offset (exclusive) of the last byte in the destination segment to be tested.
+ * @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}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code srcSegment.scope().isAccessibleBy(T) == false}.
+ * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not
+ * {@linkplain SegmentScope#isAlive() alive}.
+ * @throws WrongThreadException if this method is called from a thread {@code T},
+ * such that {@code dstSegment.scope().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
+ * {@code dstToOffset > dstSegment.byteSize()}
+ *
+ * @see MemorySegment#mismatch(MemorySegment)
+ * @see Arrays#mismatch(Object[], int, int, Object[], int, int)
+ */
+ static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOffset,
+ MemorySegment dstSegment, long dstFromOffset, long dstToOffset) {
+ return AbstractMemorySegmentImpl.mismatch(srcSegment, srcFromOffset, srcToOffset,
+ dstSegment, dstFromOffset, dstToOffset);
}
+
}
diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySession.java b/src/java.base/share/classes/java/lang/foreign/MemorySession.java
deleted file mode 100644
index c7f21293dfdeb..0000000000000
--- a/src/java.base/share/classes/java/lang/foreign/MemorySession.java
+++ /dev/null
@@ -1,285 +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.lang.ref.Cleaner;
-import java.util.Objects;
-
-import jdk.internal.foreign.MemorySessionImpl;
-import jdk.internal.javac.PreviewFeature;
-
-/**
- * A memory session manages the lifecycle of one or more resources. Resources (e.g. {@link MemorySegment}) associated
- * with a memory session can only be accessed while the memory session is {@linkplain #isAlive() alive},
- * and by the {@linkplain #ownerThread() thread} associated with the memory session (if any).
- *
- * Memory sessions can be closed. When a memory session is closed, it is no longer {@linkplain #isAlive() alive},
- * and subsequent operations on resources associated with that session (e.g. attempting to access a {@link MemorySegment} instance)
- * will fail with {@link IllegalStateException}.
- *
- * A memory session is associated with one or more {@linkplain #addCloseAction(Runnable) close actions}. Close actions
- * can be used to specify the cleanup code that must run when a given resource (or set of resources) is no longer in use.
- * When a memory session is closed, the {@linkplain #addCloseAction(Runnable) close actions}
- * associated with that session are executed (in unspecified order). For instance, closing the memory session associated with
- * one or more {@linkplain MemorySegment#allocateNative(long, long, MemorySession) native memory segments} results in releasing
- * the off-heap memory associated with said segments.
- *
- * The {@linkplain #global() global session} is a memory session that cannot be closed.
- * As a result, resources associated with the global session are never released. Examples of resources associated with
- * the global memory session are {@linkplain MemorySegment#ofArray(int[]) heap segments}.
- *
- *
Thread confinement
- *
- * Memory sessions can be divided into two categories: thread-confined memory sessions, and shared
- * memory sessions.
- *
- * Confined memory sessions, support strong thread-confinement guarantees. Upon creation,
- * they are assigned an {@linkplain #ownerThread() owner thread}, typically the thread which initiated the creation operation.
- * After creating a confined memory session, only the owner thread will be allowed to directly manipulate the resources
- * associated with this memory session. Any attempt to perform resource access from a thread other than the
- * owner thread will fail with {@link WrongThreadException}.
- *
- * Shared memory sessions, on the other hand, have no owner thread; as such, resources associated with shared memory sessions
- * can be accessed by multiple threads. This might be useful when multiple threads need to access the same resource concurrently
- * (e.g. in the case of parallel processing).
- *
- *
Closeable memory sessions
- *
- * When a session is associated with off-heap resources, it is often desirable for said resources to be released in a timely fashion,
- * rather than waiting for the session to be deemed unreachable
- * by the garbage collector. In this scenario, a client might consider using a {@linkplain #isCloseable() closeable} memory session.
- * Closeable memory sessions are memory sessions that can be {@linkplain MemorySession#close() closed} deterministically, as demonstrated
- * in the following example:
- *
- * {@snippet lang=java :
- * try (MemorySession session = MemorySession.openConfined()) {
- * MemorySegment segment1 = MemorySegment.allocateNative(100);
- * MemorySegment segment1 = MemorySegment.allocateNative(200);
- * ...
- * } // all memory released here
- * }
- *
- * The above code creates a confined, closeable session. Then it allocates two segments associated with that session.
- * When the session is {@linkplain #close() closed} (above, this is done implicitly, using the try-with-resources construct),
- * all memory allocated within the session will be released
- *
- * Closeable memory sessions, while powerful, must be used with caution. Closeable memory sessions must be closed
- * when no longer in use, either explicitly (by calling the {@link #close} method), or implicitly (by wrapping the use of
- * a closeable memory session in a try-with-resources construct). A failure to do so might result in memory leaks.
- * To mitigate this problem, closeable memory sessions can be associated with a {@link Cleaner} instance,
- * so that they are also closed automatically, once the session instance becomes unreachable.
- * This can be useful to allow for predictable, deterministic resource deallocation, while still preventing accidental
- * native memory leaks. In case a client closes a memory session managed by a cleaner, no further action will be taken when
- * the session becomes unreachable; that is, {@linkplain #addCloseAction(Runnable) close actions} associated with a
- * memory session, whether managed or not, are called exactly once.
- *
- *
Non-closeable views
- *
- * There are situations in which it might not be desirable for a memory session to be reachable from one or
- * more resources associated with it. For instance, an API might create a private memory session, and allocate
- * a memory segment, and then expose one or more slices of this segment to its clients. Since the API's memory session
- * would be reachable from the slices (using the {@link MemorySegment#session()} accessor), it might be possible for
- * clients to compromise the API (e.g. by closing the session prematurely). To avoid leaking private memory sessions
- * to untrusted clients, an API can instead return segments based on a non-closeable view of the session it created, as follows:
- *
- * {@snippet lang=java :
- * MemorySession session = MemorySession.openConfined();
- * MemorySession nonCloseableSession = session.asNonCloseable();
- * MemorySegment segment = MemorySegment.allocateNative(100, nonCloseableSession);
- * segment.session().close(); // throws
- * session.close(); //ok
- * }
- *
- * In other words, only the owner of the original {@code session} object can close the session. External clients can only
- * access the non-closeable session, and have no access to the underlying API session.
- *
- * @implSpec
- * Implementations of this interface are thread-safe.
- *
- * @see MemorySegment
- * @see SymbolLookup
- * @see Linker
- * @see VaList
- *
- * @since 19
- */
-@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public sealed interface MemorySession extends AutoCloseable, SegmentAllocator permits MemorySessionImpl, MemorySessionImpl.NonCloseableView {
-
- /**
- * {@return {@code true}, if this memory session is alive}
- */
- boolean isAlive();
-
- /**
- * {@return {@code true}, if this session is a closeable memory session}.
- */
- boolean isCloseable();
-
- /**
- * {@return the owner thread associated with this memory session, or {@code null} if this session is shared
- * across multiple threads}
- */
- Thread ownerThread();
-
- /**
- * Runs a critical action while this memory session is kept alive.
- * @param action the action to be run.
- */
- void whileAlive(Runnable action);
-
- /**
- * Adds a custom cleanup action which will be executed when the memory session is closed.
- * The order in which custom cleanup actions are invoked once the memory session is closed is unspecified.
- * @apiNote The provided action should not keep a strong reference to this memory session, so that implicitly
- * closed sessions can be handled correctly by a {@link Cleaner} instance.
- * @param runnable the custom cleanup action to be associated with this memory session.
- * @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain #ownerThread() owning} this memory session.
- */
- void addCloseAction(Runnable runnable);
-
- /**
- * Closes this memory session. If this operation completes without exceptions, this session
- * will be marked as not alive, the {@linkplain #addCloseAction(Runnable) close actions} associated
- * with this session will be executed, and all the resources associated with this session will be released.
- *
- * @apiNote This operation is not idempotent; that is, closing an already closed memory session always results in an
- * exception being thrown. This reflects a deliberate design choice: memory session state transitions should be
- * manifest in the client code; a failure in any of these transitions reveals a bug in the underlying application
- * logic.
- *
- * @see MemorySession#isAlive()
- *
- * @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}.
- * @throws IllegalStateException if this session is {@linkplain #whileAlive(Runnable) kept alive} by another client.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain #ownerThread() owning} this memory session.
- * @throws UnsupportedOperationException if this memory session is not {@linkplain #isCloseable() closeable}.
- */
- void close();
-
- /**
- * Returns a non-closeable view of this memory session. If this session is {@linkplain #isCloseable() non-closeable},
- * this session is returned. Otherwise, this method returns a non-closeable view of this memory session.
- * @apiNote a non-closeable view of a memory session {@code S} keeps {@code S} reachable. As such, {@code S}
- * cannot be closed implicitly (e.g. by a {@link Cleaner}) as long as one or more non-closeable views of {@code S}
- * are reachable.
- * @return a non-closeable view of this memory session.
- */
- MemorySession asNonCloseable();
-
- /**
- * Compares the specified object with this memory session for equality. Returns {@code true} if and only if the specified
- * object is also a memory session, and it refers to the same memory session as this memory session.
- * {@linkplain #asNonCloseable() A non-closeable view} {@code V} of a memory session {@code S} is considered
- * equal to {@code S}.
- *
- * @param that the object to be compared for equality with this memory session.
- * @return {@code true} if the specified object is equal to this memory session.
- */
- @Override
- boolean equals(Object that);
-
- /**
- * {@return the hash code value for this memory session}
- */
- @Override
- int hashCode();
-
- /**
- * Allocates a native segment, using this session. Equivalent to the following code:
- * {@snippet lang=java :
- * MemorySegment.allocateNative(size, align, this);
- * }
- *
- * @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain #ownerThread() owning} this memory session.
- * @return a new native segment, associated with this session.
- */
- @Override
- default MemorySegment allocate(long bytesSize, long bytesAlignment) {
- return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);
- }
-
- /**
- * Creates a closeable confined memory session.
- * @return a new closeable confined memory session.
- */
- static MemorySession openConfined() {
- return MemorySessionImpl.createConfined(Thread.currentThread(), null);
- }
-
- /**
- * Creates a closeable confined memory session, managed by the provided cleaner instance.
- * @param cleaner the cleaner to be associated with the returned memory session.
- * @return a new closeable confined memory session, managed by {@code cleaner}.
- */
- static MemorySession openConfined(Cleaner cleaner) {
- Objects.requireNonNull(cleaner);
- return MemorySessionImpl.createConfined(Thread.currentThread(), cleaner);
- }
-
- /**
- * Creates a closeable shared memory session.
- * @return a new closeable shared memory session.
- */
- static MemorySession openShared() {
- return MemorySessionImpl.createShared(null);
- }
-
- /**
- * Creates a closeable shared memory session, managed by the provided cleaner instance.
- * @param cleaner the cleaner to be associated with the returned memory session.
- * @return a new closeable shared memory session, managed by {@code cleaner}.
- */
- static MemorySession openShared(Cleaner cleaner) {
- Objects.requireNonNull(cleaner);
- return MemorySessionImpl.createShared(cleaner);
- }
-
- /**
- * Creates a non-closeable shared memory session, managed by a private {@link Cleaner} instance.
- * Equivalent to (but likely more efficient than) the following code:
- * {@snippet lang=java :
- * openShared(Cleaner.create()).asNonCloseable();
- * }
- * @return a non-closeable shared memory session, managed by a private {@link Cleaner} instance.
- */
- static MemorySession openImplicit() {
- return MemorySessionImpl.createImplicit();
- }
-
- /**
- * Returns the global memory session.
- * @return the global memory session.
- */
- static MemorySession global() {
- return MemorySessionImpl.GLOBAL;
- }
-}
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 0a21c804ad1ad..47848641edcc7 100644
--- a/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java
+++ b/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java
@@ -25,76 +25,30 @@
*/
package java.lang.foreign;
-import java.util.Objects;
-import java.util.Optional;
+import jdk.internal.foreign.layout.PaddingLayoutImpl;
+import jdk.internal.javac.PreviewFeature;
/**
* A padding layout. A padding layout specifies the size of extra space which is typically not accessed by applications,
* and is typically used for aligning member layouts around word boundaries.
*
* @implSpec
- * This class is immutable, thread-safe and value-based.
+ * Implementing classes are immutable, thread-safe and value-based.
+ *
+ * @since 20
*/
-/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
-
- PaddingLayout(long size) {
- this(size, 1, Optional.empty());
- }
-
- PaddingLayout(long size, long alignment, Optional name) {
- super(size, alignment, name);
- }
-
- @Override
- public String toString() {
- return decorateLayoutString("x" + bitSize());
- }
-
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (!super.equals(other)) {
- return false;
- }
- if (!(other instanceof PaddingLayout p)) {
- return false;
- }
- return bitSize() == p.bitSize();
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), bitSize());
- }
-
- @Override
- PaddingLayout dup(long alignment, Optional name) {
- return new PaddingLayout(bitSize(), alignment, name);
- }
-
- @Override
- public boolean hasNaturalAlignment() {
- return true;
- }
-
- //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
- //but that causes issues with javadoc, see JDK-8224052
+@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
+public sealed interface PaddingLayout extends MemoryLayout permits PaddingLayoutImpl {
/**
* {@inheritDoc}
*/
@Override
- public PaddingLayout withName(String name) {
- return (PaddingLayout)super.withName(name);
- }
+ PaddingLayout withName(String name);
/**
* {@inheritDoc}
*/
@Override
- public PaddingLayout withBitAlignment(long alignmentBits) {
- return (PaddingLayout)super.withBitAlignment(alignmentBits);
- }
+ 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 57bd9e704daf0..e7a3c7efb5ebb 100644
--- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java
+++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java
@@ -32,7 +32,8 @@
import java.util.Objects;
import java.util.function.Function;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
-import jdk.internal.foreign.ArenaAllocator;
+import jdk.internal.foreign.MemorySessionImpl;
+import jdk.internal.foreign.SlicingAllocator;
import jdk.internal.foreign.Utils;
import jdk.internal.javac.PreviewFeature;
@@ -45,17 +46,17 @@
*
* This interface also defines factories for commonly used allocators:
*
- *
{@link #newNativeArena(MemorySession)} creates a more efficient arena-style allocator, where off-heap memory
- * is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;
- *
{@link #implicitAllocator()} obtains an allocator which allocates native memory segment in independent,
- * {@linkplain MemorySession#openImplicit() implicit memory sessions}; and
+ *
{@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)
* and recycles its content upon each new allocation request.
*
*
* Passing a segment allocator to an API can be especially useful in circumstances where a client wants to communicate where
* the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance,
- * {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional
+ * {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handles} can accept an additional
* {@link SegmentAllocator} parameter if the underlying foreign function is known to return a struct by-value. Effectively,
* the allocator parameter tells the linker runtime where to store the return value of the foreign function.
*/
@@ -80,7 +81,7 @@ public interface SegmentAllocator {
* @implSpec the default implementation for this method copies the contents of the provided Java string
* into a new memory segment obtained by calling {@code this.allocate(str.length() + 1)}.
* @param str the Java string to be converted into a C string.
- * @return a new native memory segment containing the converted C string.
+ * @return a new native segment containing the converted C string.
*/
default MemorySegment allocateUtf8String(String str) {
Objects.requireNonNull(str);
@@ -200,11 +201,11 @@ default MemorySegment allocate(ValueLayout.OfDouble layout, double value) {
* @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, Addressable value) {
+ default MemorySegment allocate(ValueLayout.OfAddress layout, MemorySegment value) {
Objects.requireNonNull(value);
Objects.requireNonNull(layout);
MemorySegment segment = allocate(layout);
- layout.varHandle().set(segment, value.address());
+ layout.varHandle().set(segment, value);
return segment;
}
@@ -287,10 +288,8 @@ default MemorySegment allocateArray(ValueLayout.OfDouble elementLayout, double..
private MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout,
Function heapSegmentFactory) {
- Objects.requireNonNull(array);
- Objects.requireNonNull(elementLayout);
- int size = Array.getLength(array);
- MemorySegment addr = allocateArray(elementLayout, size);
+ int size = Array.getLength(Objects.requireNonNull(array));
+ MemorySegment addr = allocateArray(Objects.requireNonNull(elementLayout), size);
if (size > 0) {
MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0,
addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
@@ -327,105 +326,39 @@ default MemorySegment allocateArray(MemoryLayout elementLayout, long count) {
/**
* Allocates a memory segment with the given size.
- * @implSpec the default implementation for this method calls {@code this.allocate(bytesSize, 1)}.
- * @param bytesSize the size (in bytes) of the block of memory to be allocated.
+ * @implSpec the default implementation for this method calls {@code this.allocate(byteSize, 1)}.
+ * @param byteSize the size (in bytes) of the block of memory to be allocated.
* @return a segment for the newly allocated memory block.
- * @throws IllegalArgumentException if {@code bytesSize < 0}
+ * @throws IllegalArgumentException if {@code byteSize < 0}
*/
- default MemorySegment allocate(long bytesSize) {
- return allocate(bytesSize, 1);
+ default MemorySegment allocate(long byteSize) {
+ return allocate(byteSize, 1);
}
/**
- * Allocates a memory segment with the given size and alignment constraints.
- * @param bytesSize the size (in bytes) of the block of memory to be allocated.
- * @param bytesAlignment the alignment (in bytes) of the block of memory to be allocated.
+ * Allocates a memory segment with the given size and alignment constraint.
+ * @param byteSize the size (in bytes) of the block of memory to be allocated.
+ * @param byteAlignment the alignment (in bytes) of the block of memory to be allocated.
* @return a segment for the newly allocated memory block.
- * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0},
+ * @throws IllegalArgumentException if {@code byteSize < 0}, {@code byteAlignment <= 0},
* or if {@code alignmentBytes} is not a power of 2.
*/
- MemorySegment allocate(long bytesSize, long bytesAlignment);
-
- /**
- * Creates an unbounded arena-based allocator used to allocate native memory segments.
- * The returned allocator features a predefined block size and maximum arena size, and the segments it allocates
- * are associated with the provided memory session. Equivalent to the following code:
- * {@snippet lang=java :
- * SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, session);
- * }
- *
- * @param session the memory session associated with the segments allocated by the arena-based allocator.
- * @return a new unbounded arena-based allocator
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
- */
- static SegmentAllocator newNativeArena(MemorySession session) {
- return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, session);
- }
-
- /**
- * Creates an arena-based allocator used to allocate native memory segments.
- * The returned allocator features a block size set to the specified arena size, and the native segments
- * it allocates are associated with the provided memory session. Equivalent to the following code:
- * {@snippet lang=java :
- * SegmentAllocator.newNativeArena(arenaSize, arenaSize, session);
- * }
- *
- * @param arenaSize the size (in bytes) of the allocation arena.
- * @param session the memory session associated with the segments allocated by the arena-based allocator.
- * @return a new unbounded arena-based allocator
- * @throws IllegalArgumentException if {@code arenaSize <= 0}.
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
- */
- static SegmentAllocator newNativeArena(long arenaSize, MemorySession session) {
- return newNativeArena(arenaSize, arenaSize, session);
- }
+ MemorySegment allocate(long byteSize, long byteAlignment);
/**
- * Creates an arena-based allocator used to allocate native memory segments. The returned allocator features
- * the given block size {@code B} and the given arena size {@code A}, and the native segments
- * it allocates are associated with the provided memory session.
+ * Returns a segment allocator which responds to allocation requests by returning consecutive slices
+ * obtained from the provided segment. Each new allocation request will return a new slice starting at the
+ * current offset (modulo additional padding to satisfy alignment constraint), with given size.
*
- * The allocator arena is first initialized by {@linkplain MemorySegment#allocateNative(long, MemorySession) allocating} a
- * native memory segment {@code S} of size {@code B}. The allocator then responds to allocation requests in one of the following ways:
- *
- *
if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has a free
- * slice {@code S'} which fits that allocation request, return that {@code S'}.
- *
if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has no free
- * slices which fits that allocation request, allocate a new segment {@code S'}, with size {@code B},
- * and set {@code S = S'}; the allocator then tries to respond to the same allocation request again.
- *
if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'},
- * which has a sufficient size to satisfy the allocation request, and return {@code S'}.
- *
- *
- * This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the
- * cost associated with allocating a new off-heap memory region upon each allocation request.
- *
- * The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator
- * exceeds the arena size {@code A}, or the system capacity. Furthermore, the returned allocator is not thread safe.
- * Concurrent allocation needs to be guarded with synchronization primitives.
+ * When the returned allocator cannot satisfy an allocation request, e.g. because a slice of the provided
+ * segment with the requested size cannot be found, an {@link IndexOutOfBoundsException} is thrown.
*
- * @param arenaSize the size (in bytes) of the allocation arena.
- * @param blockSize the block size associated with the arena-based allocator.
- * @param session the memory session associated with the segments returned by the arena-based allocator.
- * @return a new unbounded arena-based allocator
- * @throws IllegalArgumentException if {@code blockSize <= 0}, if {@code arenaSize <= 0} or if {@code arenaSize < blockSize}.
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
+ * @param segment the segment which the returned allocator should slice from.
+ * @return a new slicing allocator
*/
- static SegmentAllocator newNativeArena(long arenaSize, long blockSize, MemorySession session) {
- Objects.requireNonNull(session);
- if (blockSize <= 0) {
- throw new IllegalArgumentException("Invalid block size: " + blockSize);
- }
- if (arenaSize <= 0 || arenaSize < blockSize) {
- throw new IllegalArgumentException("Invalid arena size: " + arenaSize);
- }
- return new ArenaAllocator(blockSize, arenaSize, session);
+ static SegmentAllocator slicingAllocator(MemorySegment segment) {
+ Objects.requireNonNull(segment);
+ return new SlicingAllocator(segment);
}
/**
@@ -449,24 +382,33 @@ static SegmentAllocator newNativeArena(long arenaSize, long blockSize, MemorySes
* @return an allocator which recycles an existing segment upon each new allocation request.
*/
static SegmentAllocator prefixAllocator(MemorySegment segment) {
- Objects.requireNonNull(segment);
- return (AbstractMemorySegmentImpl)segment;
+ return (AbstractMemorySegmentImpl)Objects.requireNonNull(segment);
}
/**
- * Returns an allocator which allocates native segments in independent {@linkplain MemorySession#openImplicit() implicit memory sessions}.
- * Equivalent to (but likely more efficient than) the following code:
- * {@snippet lang=java :
- * SegmentAllocator implicitAllocator = (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit());
+ * 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);
* }
- *
- * @return an allocator which allocates native segments in independent {@linkplain MemorySession#openImplicit() implicit memory sessions}.
+ * @param scope the scope associated with the segments returned by the native allocator.
+ * @return a simple allocator used to allocate native segments.
*/
- static SegmentAllocator implicitAllocator() {
- class Holder {
- static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) ->
- MemorySegment.allocateNative(size, align, MemorySession.openImplicit());
- }
- return Holder.IMPLICIT_ALLOCATOR;
+ 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
new file mode 100644
index 0000000000000..a85dec0fb2826
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/foreign/SegmentScope.java
@@ -0,0 +1,132 @@
+/*
+ * 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 d25ba35ead1e3..e73cac56851c6 100644
--- a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java
+++ b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java
@@ -25,9 +25,7 @@
*/
package java.lang.foreign;
-import java.util.Objects;
-import java.util.Optional;
-
+import jdk.internal.foreign.layout.SequenceLayoutImpl;
import jdk.internal.javac.PreviewFeature;
/**
@@ -55,46 +53,27 @@
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public final class SequenceLayout extends AbstractLayout implements MemoryLayout {
-
- private final long elemCount;
- private final MemoryLayout elementLayout;
-
- SequenceLayout(long elemCount, MemoryLayout elementLayout) {
- this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
- }
+public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayoutImpl {
- SequenceLayout(long elemCount, MemoryLayout elementLayout, long alignment, Optional name) {
- super(Math.multiplyExact(elemCount, elementLayout.bitSize()), alignment, name);
- this.elemCount = elemCount;
- this.elementLayout = elementLayout;
- }
/**
* {@return the element layout associated with this sequence layout}
*/
- public MemoryLayout elementLayout() {
- return elementLayout;
- }
+ MemoryLayout elementLayout();
/**
* {@return the element count of this sequence layout}
*/
- public long elementCount() {
- return elemCount;
- }
+ long elementCount();
/**
- * Returns a sequence layout with the same element layout, alignment constraints and name as this sequence layout,
+ * Returns a sequence layout with the same element layout, alignment constraint and name as this sequence layout,
* but with the specified element count.
* @param elementCount the new element count.
* @return a sequence layout with the given element count.
* @throws IllegalArgumentException if {@code elementCount < 0}.
*/
- public SequenceLayout withElementCount(long elementCount) {
- AbstractLayout.checkSize(elementCount, true);
- return new SequenceLayout(elementCount, elementLayout, alignment, name());
- }
+ SequenceLayout withElementCount(long elementCount);
/**
* Re-arrange the elements in this sequence layout into a multi-dimensional sequence layout.
@@ -129,47 +108,7 @@ public SequenceLayout withElementCount(long elementCount) {
* multiplying the element counts does not yield the same element count as the flattened projection of this
* sequence layout.
*/
- public SequenceLayout reshape(long... elementCounts) {
- Objects.requireNonNull(elementCounts);
- if (elementCounts.length == 0) {
- throw new IllegalArgumentException();
- }
- SequenceLayout flat = flatten();
- long expectedCount = flat.elementCount();
-
- long actualCount = 1;
- int inferPosition = -1;
- for (int i = 0 ; i < elementCounts.length ; i++) {
- if (elementCounts[i] == -1) {
- if (inferPosition == -1) {
- inferPosition = i;
- } else {
- throw new IllegalArgumentException("Too many unspecified element counts");
- }
- } else if (elementCounts[i] <= 0) {
- throw new IllegalArgumentException("Invalid element count: " + elementCounts[i]);
- } else {
- actualCount = elementCounts[i] * actualCount;
- }
- }
-
- // infer an unspecified element count (if any)
- if (inferPosition != -1) {
- long inferredCount = expectedCount / actualCount;
- elementCounts[inferPosition] = inferredCount;
- actualCount = actualCount * inferredCount;
- }
-
- if (actualCount != expectedCount) {
- throw new IllegalArgumentException("Element counts do not match expected size: " + expectedCount);
- }
-
- MemoryLayout res = flat.elementLayout();
- for (int i = elementCounts.length - 1 ; i >= 0 ; i--) {
- res = MemoryLayout.sequenceLayout(elementCounts[i], res);
- }
- return (SequenceLayout)res;
- }
+ SequenceLayout reshape(long... elementCounts);
/**
* Returns a flattened sequence layout. The element layout of the returned sequence layout
@@ -187,66 +126,11 @@ public SequenceLayout reshape(long... elementCounts) {
* @return a sequence layout with the same size as this layout (but, possibly, with different
* element count), whose element layout is not a sequence layout.
*/
- public SequenceLayout flatten() {
- long count = elementCount();
- MemoryLayout elemLayout = elementLayout();
- while (elemLayout instanceof SequenceLayout elemSeq) {
- count = count * elemSeq.elementCount();
- elemLayout = elemSeq.elementLayout();
- }
- return MemoryLayout.sequenceLayout(count, elemLayout);
- }
-
- @Override
- public String toString() {
- return decorateLayoutString(String.format("[%s:%s]",
- elemCount, elementLayout));
- }
-
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (!super.equals(other)) {
- return false;
- }
- return other instanceof SequenceLayout otherSeq &&
- elemCount == otherSeq.elemCount &&
- elementLayout.equals(otherSeq.elementLayout);
- }
+ SequenceLayout flatten();
@Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), elemCount, elementLayout);
- }
+ SequenceLayout withName(String name);
@Override
- SequenceLayout dup(long alignment, Optional name) {
- return new SequenceLayout(elementCount(), elementLayout, alignment, name);
- }
-
- @Override
- boolean hasNaturalAlignment() {
- return alignment == elementLayout.bitAlignment();
- }
-
- //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
- //but that causes issues with javadoc, see JDK-8224052
-
- /**
- * {@inheritDoc}
- */
- @Override
- public SequenceLayout withName(String name) {
- return (SequenceLayout)super.withName(name);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public SequenceLayout withBitAlignment(long alignmentBits) {
- return (SequenceLayout)super.withBitAlignment(alignmentBits);
- }
+ 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
new file mode 100644
index 0000000000000..b94b6a97d85e5
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/foreign/StructLayout.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019, 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.layout.StructLayoutImpl;
+import jdk.internal.javac.PreviewFeature;
+
+/**
+ * A group layout whose member layouts are laid out one after the other.
+ *
+ * @implSpec
+ * Implementing classes are immutable, thread-safe and value-based.
+ *
+ * @since 20
+ */
+@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
+public sealed interface StructLayout extends GroupLayout permits StructLayoutImpl {
+
+ @Override
+ StructLayout withName(String name);
+
+ @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 c6fb83b61a969..50575f6b106d4 100644
--- a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java
+++ b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java
@@ -42,35 +42,35 @@
import java.util.function.BiFunction;
/**
- * A symbol lookup is an object that may be used to retrieve the address of a symbol in one or more libraries.
+ * A symbol lookup retrieves the address of a symbol in one or more libraries.
* A symbol is a named entity, such as a function or a global variable.
*
- * A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#lookup(String)}
+ * A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#find(String)}
* method takes the name of a symbol and returns the address of the symbol in that library.
*
* The address of a symbol is modelled as a zero-length {@linkplain MemorySegment memory segment}. The segment can be used in different ways:
*
- *
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 base address.
- *
It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle}, as an argument to the underlying foreign function.
- *
It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, Addressable) stored} inside another memory segment.
- *
It can be used to dereference memory associated with a global variable (this might require
- * {@link MemorySegment#ofAddress(MemoryAddress, long, MemorySession) resizing} the segment first).
+ *
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).
*
*
*
Obtaining a symbol lookup
*
- * The factory methods {@link #libraryLookup(String, MemorySession)} and {@link #libraryLookup(Path, MemorySession)}
+ * The factory methods {@link #libraryLookup(String, SegmentScope)} and {@link #libraryLookup(Path, SegmentScope)}
* 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 MemorySession memory session}; when the session is {@linkplain MemorySession#close() closed}, the library is unloaded:
+ * with a {@linkplain SegmentScope scope}; when the scope becomes not {@link SegmentScope#isAlive()}, the library is unloaded:
*
- * {@snippet lang=java :
- * try (MemorySession session = MemorySession.openConfined()) {
- * SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so"); // libGL.so loaded here
- * MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow();
+ * {@snippet lang = java:
+ * try (Arena arena = Arena.openConfined()) {
+ * SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena.scope()); // libGL.so loaded here
+ * MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
* ...
* } // libGL.so unloaded here
- * }
+ *}
*
* If a library was previously loaded through JNI, i.e., by {@link System#load(String)}
* or {@link System#loadLibrary(String)}, then the library was also associated with a particular class loader. The factory
@@ -80,7 +80,7 @@
* System.loadLibrary("GL"); // libGL.so loaded here
* ...
* SymbolLookup libGL = SymbolLookup.loaderLookup();
- * MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow();
+ * MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
* }
*
* This symbol lookup, which is known as a loader lookup, is dynamic with respect to the libraries associated
@@ -91,18 +91,18 @@
* by {@link System#load(String)} or {@link System#loadLibrary(String)}. A loader lookup does not expose symbols in libraries
* that were loaded in the course of creating a library lookup:
*
- * {@snippet lang=java :
- * libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true
- * loaderLookup().lookup("glGetString").isPresent(); // false
- * }
+ * {@snippet lang = java:
+ * libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
+ * loaderLookup().find("glGetString").isPresent(); // false
+ *}
*
* Note also that a library lookup for library {@code L} exposes symbols in {@code L} even if {@code L} was previously loaded
* through JNI (the association with a class loader is immaterial to the library lookup):
*
- * {@snippet lang=java :
+ * {@snippet lang = java:
* System.loadLibrary("GL"); // libGL.so loaded here
- * libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true
- * }
+ * libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
+ *}
*
*
* Finally, each {@link Linker} provides a symbol lookup for libraries that are commonly used on the OS and processor
@@ -110,11 +110,11 @@
* helps clients to quickly find addresses of well-known symbols. For example, a {@link Linker} for Linux/x64 might choose to
* expose symbols in {@code libc} through the default lookup:
*
- * {@snippet lang=java :
+ * {@snippet lang = java:
* Linker nativeLinker = Linker.nativeLinker();
* SymbolLookup stdlib = nativeLinker.defaultLookup();
- * MemorySegment malloc = stdlib.lookup("malloc").orElseThrow();
- * }
+ * MemorySegment malloc = stdlib.find("malloc").orElseThrow();
+ *}
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
@FunctionalInterface
@@ -123,9 +123,9 @@ public interface SymbolLookup {
/**
* Returns the address of the symbol with the given name.
* @param name the symbol name.
- * @return a zero-length memory segment whose base address indicates the address of the symbol, if found.
+ * @return a zero-length memory segment whose address indicates the address of the symbol, if found.
*/
- Optional lookup(String name);
+ Optional find(String name);
/**
* Returns a symbol lookup for symbols in the libraries associated with the caller's class loader.
@@ -139,8 +139,8 @@ 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 {@linkplain MemorySession#asNonCloseable() non-closeable}, shared memory
- * session which keeps the caller's class loader reachable. Therefore, libraries associated with the caller's class
+ * 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.
*
* In cases where this method is called from a context where there is no caller frame on the stack
@@ -158,24 +158,24 @@ static SymbolLookup loaderLookup() {
ClassLoader loader = caller != null ?
caller.getClassLoader() :
ClassLoader.getSystemClassLoader();
- MemorySession loaderSession = (loader == null || loader instanceof BuiltinClassLoader) ?
- MemorySession.global() : // builtin loaders never go away
+ SegmentScope loaderScope = (loader == null || loader instanceof BuiltinClassLoader) ?
+ SegmentScope.global() : // builtin loaders never go away
MemorySessionImpl.heapSession(loader);
return name -> {
Objects.requireNonNull(name);
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
// note: ClassLoader::findNative supports a null loader
- MemoryAddress addr = MemoryAddress.ofLong(javaLangAccess.findNative(loader, name));
- return addr == MemoryAddress.NULL ?
+ long addr = javaLangAccess.findNative(loader, name);
+ return addr == 0L ?
Optional.empty() :
- Optional.of(MemorySegment.ofAddress(addr, 0L, loaderSession));
+ Optional.of(MemorySegment.ofAddress(addr, 0L, loaderScope));
};
}
/**
* 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 memory session is {@linkplain MemorySession#close() closed},
- * if no other library lookup is still using it.
+ * The library will be unloaded when the provided scope becomes
+ * not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
* @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,7 +186,7 @@ static SymbolLookup loaderLookup() {
* 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 session the memory session which controls the library lifecycle.
+ * @param scope the scope 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 IllegalArgumentException if {@code name} does not identify a valid library.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
@@ -194,15 +194,15 @@ static SymbolLookup loaderLookup() {
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
- static SymbolLookup libraryLookup(String name, MemorySession session) {
+ static SymbolLookup libraryLookup(String name, SegmentScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
- return libraryLookup(name, RawNativeLibraries::load, session);
+ return libraryLookup(name, RawNativeLibraries::load, scope);
}
/**
* 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 memory session is {@linkplain MemorySession#close() closed},
- * if no other library lookup is still using it.
+ * 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.
*
* This method is restricted.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
@@ -212,7 +212,7 @@ static SymbolLookup libraryLookup(String name, MemorySession session) {
* @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 session the memory session which controls the library lifecycle.
+ * @param scope the scope 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 IllegalArgumentException if {@code path} does not point to a valid library.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
@@ -220,22 +220,22 @@ static SymbolLookup libraryLookup(String name, MemorySession session) {
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
- static SymbolLookup libraryLookup(Path path, MemorySession session) {
+ static SymbolLookup libraryLookup(Path path, SegmentScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
- return libraryLookup(path, RawNativeLibraries::load, session);
+ return libraryLookup(path, RawNativeLibraries::load, scope);
}
- private static SymbolLookup libraryLookup(Z libDesc, BiFunction loadLibraryFunc, MemorySession session) {
+ private static SymbolLookup libraryLookup(Z libDesc, BiFunction loadLibraryFunc, SegmentScope libScope) {
Objects.requireNonNull(libDesc);
- Objects.requireNonNull(session);
+ Objects.requireNonNull(libScope);
// attempt to load native library from path or name
RawNativeLibraries nativeLibraries = RawNativeLibraries.newInstance(MethodHandles.lookup());
NativeLibrary library = loadLibraryFunc.apply(nativeLibraries, libDesc);
if (library == null) {
throw new IllegalArgumentException("Cannot open library: " + libDesc);
}
- // register hook to unload library when session is closed
- MemorySessionImpl.toSessionImpl(session).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
+ // register hook to unload library when 'libScope' becomes not alive
+ ((MemorySessionImpl) libScope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
nativeLibraries.unload(library);
@@ -243,10 +243,10 @@ public void cleanup() {
});
return name -> {
Objects.requireNonNull(name);
- MemoryAddress addr = MemoryAddress.ofLong(library.find(name));
- return addr == MemoryAddress.NULL
- ? Optional.empty() :
- Optional.of(MemorySegment.ofAddress(addr, 0L, session));
+ long addr = library.find(name);
+ return addr == 0L ?
+ Optional.empty() :
+ Optional.of(MemorySegment.ofAddress(addr, 0, libScope));
};
}
}
diff --git a/src/java.base/share/classes/java/lang/foreign/UnionLayout.java b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java
new file mode 100644
index 0000000000000..155f47bb4df84
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, 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.layout.UnionLayoutImpl;
+import jdk.internal.javac.PreviewFeature;
+
+/**
+ * A group layout whose member layouts are laid out at the same starting offset.
+ *
+ * @implSpec
+ * Implementing classes are immutable, thread-safe and value-based.
+ *
+ * @since 20
+ */
+@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
+public sealed interface UnionLayout extends GroupLayout permits UnionLayoutImpl {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ UnionLayout withName(String name);
+
+ /**
+ * {@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
index 209ae0f878807..b366a4f8c42c8 100644
--- a/src/java.base/share/classes/java/lang/foreign/VaList.java
+++ b/src/java.base/share/classes/java/lang/foreign/VaList.java
@@ -39,10 +39,40 @@
import jdk.internal.reflect.Reflection;
/**
- * A variable argument list, similar in functionality to a C {@code va_list}.
+ * Helper class to create and manipulate variable argument lists, similar in functionality to a C {@code va_list}.
*
- * A variable argument list is a stateful cursor used to iterate over a set of arguments. A variable argument list
- * can be passed by reference e.g. to a {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle}.
+ * 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',
@@ -57,22 +87,17 @@
*
* 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, MemorySession)} are capable of detecting out-of-bounds reads;
- *
Variable argument lists created unsafely, using {@link #ofAddress(MemoryAddress, MemorySession)} are not capable of detecting out-of-bounds reads
+ *
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 memory session associated with the variable arity list).
+ * (regardless of the scope used to obtain the variable arity list).
*
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-sealed public interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList {
-
- /**
- * {@return the memory session associated with this variable argument list}
- */
- MemorySession session();
+public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList {
/**
* Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this
@@ -80,10 +105,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
*
* @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 {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with 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);
@@ -94,10 +119,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
*
* @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 {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with 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);
@@ -108,32 +133,37 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
*
* @param layout the layout of the value
* @return the {@code double} value read from this variable argument list.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with 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 value as a {@code MemoryAddress} and advances this variable argument list's position. The behavior of this
- * method is equivalent to the C {@code va_arg} function.
+ * 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 the {@code MemoryAddress} value read from this variable argument list.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this variable argument list.
+ * @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.
*/
- MemoryAddress nextVarg(ValueLayout.OfAddress layout);
+ MemorySegment nextVarg(ValueLayout.OfAddress layout);
/**
- * Reads the next value as a {@code MemorySegment}, 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.
+ * 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
@@ -145,10 +175,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* @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 {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with 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);
@@ -157,17 +187,17 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* 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 {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with 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.
*/
void skip(MemoryLayout... layouts);
/**
* Copies this variable argument list at its current position into a new variable argument list associated
- * with the same memory session as this variable argument list. The behavior of this method is equivalent to the C
+ * 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,
@@ -175,59 +205,57 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* traversed multiple times.
*
* @return a copy of this variable argument list.
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with 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();
/**
- * {@return the {@linkplain MemoryAddress memory address} associated with this variable argument list}
- * @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
- * {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread owning
- * the {@linkplain #session() session} associated with this variable argument list.
+ * 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.
*/
- @Override
- MemoryAddress address();
+ MemorySegment segment();
/**
- * Creates a variable argument list from a memory address pointing to an existing variable argument list,
- * with the given memory session.
+ * 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.
*
- * @implNote variable argument lists created using this method can not detect out-of-bounds reads.
- *
- * @param address a memory address pointing to an existing variable argument list.
- * @param session the memory session to be associated with the returned variable argument list.
- * @return a new variable argument list backed by the memory region at {@code address}.
- * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
+ * @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 access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
- static VaList ofAddress(MemoryAddress address, MemorySession session) {
+ static VaList ofAddress(long address, SegmentScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), VaList.class, "ofAddress");
- Objects.requireNonNull(address);
- Objects.requireNonNull(session);
- return SharedUtils.newVaListOfAddress(address, session);
+ Objects.requireNonNull(scope);
+ return SharedUtils.newVaListOfAddress(address, scope);
}
/**
* Creates a variable argument list using a builder (see {@link Builder}), with the given
- * memory session.
- *
- * If this method needs to allocate memory, such memory will be managed by the given
- * memory session, and will be released when the memory session is {@linkplain MemorySession#close closed}.
+ * 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()}.
@@ -236,23 +264,23 @@ static VaList ofAddress(MemoryAddress address, MemorySession session) {
*
* @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 session the memory session to be associated with the new variable arity 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 session} is not {@linkplain MemorySession#isAlive() alive}.
- * @throws WrongThreadException if this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} {@code session}.
+ * @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, MemorySession session) {
+ static VaList make(Consumer actions, SegmentScope scope) {
Objects.requireNonNull(actions);
- Objects.requireNonNull(session);
- return SharedUtils.newVaList(actions, session);
+ Objects.requireNonNull(scope);
+ return SharedUtils.newVaList(actions, scope);
}
/**
- * Returns an empty variable argument list, associated with the {@linkplain MemorySession#global() global}
- * memory session. The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException}
- * on all operations, except for {@link VaList#address()}, {@link VaList#copy()} and {@link VaList#session()}.
+ * 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.
*/
@@ -296,16 +324,17 @@ sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAAr
Builder addVarg(ValueLayout.OfDouble layout, double value);
/**
- * Writes an {@code Addressable} value to the variable argument list being constructed.
+ * 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 value the {@code Addressable} 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, Addressable value);
+ Builder addVarg(ValueLayout.OfAddress layout, MemorySegment segment);
/**
- * Writes a {@code MemorySegment} value, with the given layout, to the variable argument list being constructed.
+ * 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.
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 0bbea651c38e8..f9a624195989e 100644
--- a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java
+++ b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java
@@ -28,102 +28,49 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import jdk.internal.foreign.Utils;
+import jdk.internal.foreign.layout.ValueLayouts;
import jdk.internal.javac.PreviewFeature;
-import jdk.internal.misc.Unsafe;
-import jdk.internal.vm.annotation.ForceInline;
-import jdk.internal.vm.annotation.Stable;
-import sun.invoke.util.Wrapper;
+import jdk.internal.reflect.CallerSensitive;
/**
- * A value layout. A value layout is used to model the memory layout associated with values of basic data types, such as integral types
- * (either signed or unsigned) and floating-point types. Each value layout has a size, an alignment (in bits),
+ * A layout that models values of basic data types. Examples of values modelled by a value layout are
+ * integral values (either signed or unsigned), floating-point values and
+ * address values.
+ *
+ * Each value layout has a size, an alignment (in bits),
* a {@linkplain ByteOrder byte order}, and a carrier, that is, the Java type that should be used when
- * {@linkplain MemorySegment#get(OfInt, long) accessing} a memory region using the value layout.
+ * {@linkplain MemorySegment#get(OfInt, long) accessing} a region of memory using the value layout.
*
* This class defines useful value layout constants for Java primitive types and addresses.
* The layout constants in this class make implicit alignment and byte-ordering assumption: all layout
* constants in this class are byte-aligned, and their byte order is set to the {@linkplain ByteOrder#nativeOrder() platform default},
* thus making it easy to work with other APIs, such as arrays and {@link java.nio.ByteBuffer}.
*
- * @implSpec
- * This class and its subclasses are immutable, thread-safe and value-based.
+ * @implSpec implementing classes and subclasses are immutable, thread-safe and value-based.
*
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
-public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
-
- private final Class> carrier;
- private final ByteOrder order;
-
- private static final int ADDRESS_SIZE_BITS = Unsafe.ADDRESS_SIZE * 8;
-
- ValueLayout(Class> carrier, ByteOrder order, long size) {
- this(carrier, order, size, size, Optional.empty());
- }
-
- ValueLayout(Class> carrier, ByteOrder order, long size, long alignment, Optional name) {
- super(size, alignment, name);
- this.carrier = carrier;
- this.order = order;
- checkCarrierSize(carrier, size);
- }
+public sealed interface ValueLayout extends MemoryLayout {
/**
* {@return the value's byte order}
*/
- public ByteOrder order() {
- return order;
- }
+ ByteOrder order();
/**
- * Returns a value layout with the same carrier, alignment constraints and name as this value layout,
+ * Returns a value layout with the same carrier, alignment constraint and name as this value layout,
* but with the specified byte order.
*
* @param order the desired byte order.
* @return a value layout with the given byte order.
*/
- public ValueLayout withOrder(ByteOrder order) {
- return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name());
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- char descriptor = carrier == MemoryAddress.class ? 'A' : carrier.descriptorString().charAt(0);
- if (order == ByteOrder.LITTLE_ENDIAN) {
- descriptor = Character.toLowerCase(descriptor);
- }
- return decorateLayoutString(String.format("%s%d", descriptor, bitSize()));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (!super.equals(other)) {
- return false;
- }
- return other instanceof ValueLayout otherValue &&
- carrier.equals(otherValue.carrier) &&
- order.equals(otherValue.order);
- }
+ ValueLayout withOrder(ByteOrder order);
/**
- * Creates a strided access var handle that can be used to dereference a multi-dimensional array. The
- * layout of this array is a sequence layout with {@code shape.length} nested sequence layouts. The element
+ * 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
* layout of the sequence layout at depth {@code shape.length} is this value layout.
* As a result, if {@code shape.length == 0}, the array layout will feature only one dimension.
*
@@ -138,15 +85,15 @@ public boolean equals(Object other) {
*
* Can be used to access a multi-dimensional array whose layout is as follows:
*
- * {@snippet lang=java :
- * SequenceLayout arrayLayout = MemoryLayout.sequenceLayout(-1,
+ * {@snippet lang = java:
+ * SequenceLayout arrayLayout = MemoryLayout.sequenceLayout(
* MemoryLayout.sequenceLayout(10,
* MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT)));
- * }
+ *}
*
* The resulting var handle {@code arrayHandle} will feature 3 coordinates of type {@code long}; each coordinate
* is interpreted as an index into the corresponding sequence layout. If we refer to the var handle coordinates, from left
- * to right, as {@code x}, {@code y} and {@code z} respectively, the final offset dereferenced by the var handle can be
+ * to right, as {@code x}, {@code y} and {@code z} respectively, the final offset accessed by the var handle can be
* computed with the following formula:
*
*
{@code
@@ -170,7 +117,8 @@ public boolean equals(Object other) {
* as the value for {@code z} is outside its specified bounds.
*
* @param shape the size of each nested array dimension.
- * @return a var handle which can be used to dereference a multi-dimensional array, featuring {@code shape.length + 1}
+ * @return a var handle which can be used to access a memory segment as a multi-dimensional array,
+ * featuring {@code shape.length + 1}
* {@code long} coordinates.
* @throws IllegalArgumentException if {@code shape[i] < 0}, for at least one index {@code i}.
* @throws UnsupportedOperationException if {@code bitAlignment() > bitSize()}.
@@ -178,504 +126,385 @@ public boolean equals(Object other) {
* @see MemoryLayout#varHandle(PathElement...)
* @see SequenceLayout
*/
- public VarHandle arrayElementVarHandle(int... shape) {
- Objects.requireNonNull(shape);
- MemoryLayout layout = this;
- List path = new ArrayList<>();
- for (int i = shape.length ; i > 0 ; i--) {
- int size = shape[i - 1];
- if (size < 0) throw new IllegalArgumentException("Invalid shape size: " + size);
- layout = MemoryLayout.sequenceLayout(size, layout);
- path.add(PathElement.sequenceElement());
- }
- layout = MemoryLayout.sequenceLayout(-1, layout);
- path.add(PathElement.sequenceElement());
- return layout.varHandle(path.toArray(new PathElement[0]));
- }
+ VarHandle arrayElementVarHandle(int... shape);
/**
* {@return the carrier associated with this value layout}
*/
- public Class> carrier() {
- return carrier;
- }
+ Class> carrier();
/**
* {@inheritDoc}
*/
@Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), order, carrier);
- }
-
- @Override
- ValueLayout dup(long alignment, Optional name) {
- return new ValueLayout(carrier, order, bitSize(), alignment, name());
- }
-
- //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
- //but that causes issues with javadoc, see JDK-8224052
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ValueLayout withName(String name) {
- return (ValueLayout)super.withName(name);
- }
+ ValueLayout withName(String name);
/**
* {@inheritDoc}
*/
@Override
- public ValueLayout withBitAlignment(long alignmentBits) {
- return (ValueLayout)super.withBitAlignment(alignmentBits);
- }
- static void checkCarrierSize(Class> carrier, long size) {
- if (!isValidCarrier(carrier)) {
- throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
- }
- if (carrier == MemoryAddress.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);
- }
- }
- }
-
- static boolean isValidCarrier(Class> carrier) {
- return carrier == boolean.class
- || carrier == byte.class
- || carrier == short.class
- || carrier == char.class
- || carrier == int.class
- || carrier == long.class
- || carrier == float.class
- || carrier == double.class
- || carrier == MemoryAddress.class;
- }
-
- @Stable
- private VarHandle handle;
-
- @ForceInline
- VarHandle accessHandle() {
- if (handle == null) {
- // this store to stable field is safe, because return value of 'makeMemoryAccessVarHandle' has stable identity
- handle = Utils.makeSegmentViewVarHandle(this);
- }
- return handle;
- }
+ ValueLayout withBitAlignment(long bitAlignment);
/**
* A value layout whose carrier is {@code boolean.class}.
*
+ * @see #JAVA_BOOLEAN
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfBoolean extends ValueLayout {
- OfBoolean(ByteOrder order) {
- super(boolean.class, order, 8);
- }
-
- OfBoolean(ByteOrder order, long alignment, Optional name) {
- super(boolean.class, order, 8, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfBoolean extends ValueLayout permits ValueLayouts.OfBooleanImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfBoolean dup(long alignment, Optional name) {
- return new OfBoolean(order(), alignment, name);
- }
+ OfBoolean withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfBoolean withName(String name) {
- return (OfBoolean)super.withName(name);
- }
+ OfBoolean withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfBoolean withBitAlignment(long alignmentBits) {
- return (OfBoolean)super.withBitAlignment(alignmentBits);
- }
+ OfBoolean withOrder(ByteOrder order);
- @Override
- public OfBoolean withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfBoolean(order, alignment, name());
- }
}
/**
* A value layout whose carrier is {@code byte.class}.
*
+ * @see #JAVA_BYTE
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfByte extends ValueLayout {
- OfByte(ByteOrder order) {
- super(byte.class, order, 8);
- }
-
- OfByte(ByteOrder order, long alignment, Optional name) {
- super(byte.class, order, 8, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfByte extends ValueLayout permits ValueLayouts.OfByteImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfByte dup(long alignment, Optional name) {
- return new OfByte(order(), alignment, name);
- }
+ OfByte withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfByte withName(String name) {
- return (OfByte)super.withName(name);
- }
+ OfByte withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfByte withBitAlignment(long alignmentBits) {
- return (OfByte)super.withBitAlignment(alignmentBits);
- }
+ OfByte withOrder(ByteOrder order);
- @Override
- public OfByte withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfByte(order, alignment, name());
- }
}
/**
* A value layout whose carrier is {@code char.class}.
*
+ * @see #JAVA_CHAR
+ * @see #JAVA_CHAR_UNALIGNED
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfChar extends ValueLayout {
- OfChar(ByteOrder order) {
- super(char.class, order, 16);
- }
-
- OfChar(ByteOrder order, long alignment, Optional name) {
- super(char.class, order, 16, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfChar extends ValueLayout permits ValueLayouts.OfCharImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfChar dup(long alignment, Optional name) {
- return new OfChar(order(), alignment, name);
- }
+ OfChar withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfChar withName(String name) {
- return (OfChar)super.withName(name);
- }
+ OfChar withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfChar withBitAlignment(long alignmentBits) {
- return (OfChar)super.withBitAlignment(alignmentBits);
- }
+ OfChar withOrder(ByteOrder order);
- @Override
- public OfChar withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfChar(order, alignment, name());
- }
}
/**
* A value layout whose carrier is {@code short.class}.
*
+ * @see #JAVA_SHORT
+ * @see #JAVA_SHORT_UNALIGNED
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfShort extends ValueLayout {
- OfShort(ByteOrder order) {
- super(short.class, order, 16);
- }
-
- OfShort(ByteOrder order, long alignment, Optional name) {
- super(short.class, order, 16, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfShort extends ValueLayout permits ValueLayouts.OfShortImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfShort dup(long alignment, Optional name) {
- return new OfShort(order(), alignment, name);
- }
+ OfShort withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfShort withName(String name) {
- return (OfShort)super.withName(name);
- }
+ OfShort withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfShort withBitAlignment(long alignmentBits) {
- return (OfShort)super.withBitAlignment(alignmentBits);
- }
+ OfShort withOrder(ByteOrder order);
- @Override
- public OfShort withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfShort(order, alignment, name());
- }
}
/**
* A value layout whose carrier is {@code int.class}.
*
+ * @see #JAVA_INT
+ * @see #JAVA_INT_UNALIGNED
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfInt extends ValueLayout {
- OfInt(ByteOrder order) {
- super(int.class, order, 32);
- }
-
- OfInt(ByteOrder order, long alignment, Optional name) {
- super(int.class, order, 32, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfInt extends ValueLayout permits ValueLayouts.OfIntImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfInt dup(long alignment, Optional name) {
- return new OfInt(order(), alignment, name);
- }
+ OfInt withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfInt withName(String name) {
- return (OfInt)super.withName(name);
- }
+ OfInt withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfInt withBitAlignment(long alignmentBits) {
- return (OfInt)super.withBitAlignment(alignmentBits);
- }
+ OfInt withOrder(ByteOrder order);
- @Override
- public OfInt withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfInt(order, alignment, name());
- }
}
/**
* A value layout whose carrier is {@code float.class}.
*
+ * @see #JAVA_FLOAT
+ * @see #JAVA_FLOAT_UNALIGNED
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfFloat extends ValueLayout {
- OfFloat(ByteOrder order) {
- super(float.class, order, 32);
- }
-
- OfFloat(ByteOrder order, long alignment, Optional name) {
- super(float.class, order, 32, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfFloat extends ValueLayout permits ValueLayouts.OfFloatImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfFloat dup(long alignment, Optional name) {
- return new OfFloat(order(), alignment, name);
- }
+ OfFloat withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfFloat withName(String name) {
- return (OfFloat)super.withName(name);
- }
+ OfFloat withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfFloat withBitAlignment(long alignmentBits) {
- return (OfFloat)super.withBitAlignment(alignmentBits);
- }
+ OfFloat withOrder(ByteOrder order);
- @Override
- public OfFloat withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfFloat(order, alignment, name());
- }
}
/**
* A value layout whose carrier is {@code long.class}.
*
+ * @see #JAVA_LONG
+ * @see #JAVA_LONG_UNALIGNED
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfLong extends ValueLayout {
- OfLong(ByteOrder order) {
- super(long.class, order, 64);
- }
-
- OfLong(ByteOrder order, long alignment, Optional name) {
- super(long.class, order, 64, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfLong extends ValueLayout permits ValueLayouts.OfLongImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfLong dup(long alignment, Optional name) {
- return new OfLong(order(), alignment, name);
- }
+ OfLong withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfLong withName(String name) {
- return (OfLong)super.withName(name);
- }
+ OfLong withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfLong withBitAlignment(long alignmentBits) {
- return (OfLong)super.withBitAlignment(alignmentBits);
- }
+ OfLong withOrder(ByteOrder order);
- @Override
- public OfLong withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfLong(order, alignment, name());
- }
}
/**
* A value layout whose carrier is {@code double.class}.
*
+ * @see #JAVA_DOUBLE
+ * @see #JAVA_DOUBLE_UNALIGNED
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfDouble extends ValueLayout {
- OfDouble(ByteOrder order) {
- super(double.class, order, 64);
- }
-
- OfDouble(ByteOrder order, long alignment, Optional name) {
- super(double.class, order, 64, alignment, name);
- }
+ @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
+ sealed interface OfDouble extends ValueLayout permits ValueLayouts.OfDoubleImpl {
+ /**
+ * {@inheritDoc}
+ */
@Override
- OfDouble dup(long alignment, Optional name) {
- return new OfDouble(order(), alignment, name);
- }
+ OfDouble withName(String name);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfDouble withName(String name) {
- return (OfDouble)super.withName(name);
- }
+ OfDouble withBitAlignment(long bitAlignment);
+ /**
+ * {@inheritDoc}
+ */
@Override
- public OfDouble withBitAlignment(long alignmentBits) {
- return (OfDouble)super.withBitAlignment(alignmentBits);
- }
+ OfDouble withOrder(ByteOrder order);
- @Override
- public OfDouble withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfDouble(order, alignment, name());
- }
}
/**
- * A value layout whose carrier is {@code MemoryAddress.class}.
+ * A value layout whose carrier is {@code MemorySegment.class}.
*
+ * @see #ADDRESS
+ * @see #ADDRESS_UNALIGNED
* @since 19
*/
- @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public static final class OfAddress extends ValueLayout {
- OfAddress(ByteOrder order) {
- super(MemoryAddress.class, order, ADDRESS_SIZE_BITS);
- }
-
- OfAddress(ByteOrder order, long size, long alignment, Optional name) {
- super(MemoryAddress.class, order, size, alignment, name);
- }
-
- @Override
- OfAddress dup(long alignment, Optional name) {
- return new OfAddress(order(), bitSize(), alignment, name);
- }
+ @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.
+ * @see #isUnbounded()
+ */
+ @CallerSensitive
+ OfAddress asUnbounded();
+
+ /**
+ * {@return {@code true}, if this address layout is an {@linkplain #asUnbounded() unbounded address layout}}.
+ */
+ boolean isUnbounded();
- @Override
- public OfAddress withName(String name) {
- return (OfAddress)super.withName(name);
- }
-
- @Override
- public OfAddress withBitAlignment(long alignmentBits) {
- return (OfAddress)super.withBitAlignment(alignmentBits);
- }
-
- @Override
- public OfAddress withOrder(ByteOrder order) {
- Objects.requireNonNull(order);
- return new OfAddress(order, bitSize(), alignment, name());
- }
}
/**
* 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}, and byte order set to {@link ByteOrder#nativeOrder()}.
+ * 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(MemoryAddress.class, ByteOrder.nativeOrder())
- * .withBitAlignment(
);
+ * MemoryLayout.valueLayout(MemorySegment.class, ByteOrder.nativeOrder());
* }
*/
- public static final OfAddress ADDRESS = new OfAddress(ByteOrder.nativeOrder())
- .withBitAlignment(ValueLayout.ADDRESS_SIZE_BITS);
+ OfAddress 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()).withBitAlignment(8);
+ * MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder());
* }
*/
- public static final OfByte JAVA_BYTE = new OfByte(ByteOrder.nativeOrder()).withBitAlignment(8);
+ 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()).withBitAlignment(8);
+ * MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder());
* }
*/
- public static final OfBoolean JAVA_BOOLEAN = new OfBoolean(ByteOrder.nativeOrder()).withBitAlignment(8);
+ 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()).withBitAlignment(16);
+ * MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder());
* }
*/
- public static final OfChar JAVA_CHAR = new OfChar(ByteOrder.nativeOrder()).withBitAlignment(16);
+ 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()).withBitAlignment(16);
+ * MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder());
* }
*/
- public static final OfShort JAVA_SHORT = new OfShort(ByteOrder.nativeOrder()).withBitAlignment(16);
+ 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()).withBitAlignment(32);
+ * MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder());
* }
*/
- public static final OfInt JAVA_INT = new OfInt(ByteOrder.nativeOrder()).withBitAlignment(32);
+ 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()).withBitAlignment(64);
+ * MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder());
* }
*/
- public static final OfLong JAVA_LONG = new OfLong(ByteOrder.nativeOrder())
- .withBitAlignment(64);
+ OfLong JAVA_LONG = ValueLayouts.OfLongImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code float},
@@ -685,15 +514,100 @@ public OfAddress withOrder(ByteOrder order) {
* MemoryLayout.valueLayout(float.class, ByteOrder.nativeOrder()).withBitAlignment(32);
* }
*/
- public static final OfFloat JAVA_FLOAT = new OfFloat(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()).withBitAlignment(64);
+ * MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder());
+ * }
+ */
+ OfDouble JAVA_DOUBLE = ValueLayouts.OfDoubleImpl.of(ByteOrder.nativeOrder());
+
+ /**
+ * An unaligned value layout constant whose size is the same as that of a machine address ({@code size_t}),
+ * and byte order set to {@link ByteOrder#nativeOrder()}.
+ * Equivalent to the following code:
+ * {@snippet lang=java :
+ * ADDRESS.withBitAlignment(8);
+ * }
+ * @apiNote Care should be taken when using unaligned value layouts as they may induce
+ * performance and portability issues.
+ */
+ OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8);
+
+ /**
+ * An unaligned value layout constant whose size is the same as that of a Java {@code char}
+ * and byte order set to {@link ByteOrder#nativeOrder()}.
+ * Equivalent to the following code:
+ * {@snippet lang=java :
+ * JAVA_CHAR.withBitAlignment(8);
* }
+ * @apiNote Care should be taken when using unaligned value layouts as they may induce
+ * performance and portability issues.
*/
- public static final OfDouble JAVA_DOUBLE = new OfDouble(ByteOrder.nativeOrder()).withBitAlignment(64);
+ OfChar JAVA_CHAR_UNALIGNED = JAVA_CHAR.withBitAlignment(8);
+
+ /**
+ * An unaligned value layout constant whose size is the same as that of a Java {@code short}
+ * and byte order set to {@link ByteOrder#nativeOrder()}.
+ * Equivalent to the following code:
+ * {@snippet lang=java :
+ * JAVA_SHORT.withBitAlignment(8);
+ * }
+ * @apiNote Care should be taken when using unaligned value layouts as they may induce
+ * performance and portability issues.
+ */
+ OfShort JAVA_SHORT_UNALIGNED = JAVA_SHORT.withBitAlignment(8);
+
+ /**
+ * An unaligned value layout constant whose size is the same as that of a Java {@code int}
+ * and byte order set to {@link ByteOrder#nativeOrder()}.
+ * Equivalent to the following code:
+ * {@snippet lang=java :
+ * JAVA_INT.withBitAlignment(8);
+ * }
+ * @apiNote Care should be taken when using unaligned value layouts as they may induce
+ * performance and portability issues.
+ */
+ OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8);
+
+ /**
+ * An unaligned value layout constant whose size is the same as that of a Java {@code long}
+ * and byte order set to {@link ByteOrder#nativeOrder()}.
+ * Equivalent to the following code:
+ * {@snippet lang=java :
+ * JAVA_LONG.withBitAlignment(8);
+ * }
+ * @apiNote Care should be taken when using unaligned value layouts as they may induce
+ * performance and portability issues.
+ */
+ OfLong JAVA_LONG_UNALIGNED = JAVA_LONG.withBitAlignment(8);
+
+ /**
+ * An unaligned value layout constant whose size is the same as that of a Java {@code float}
+ * and byte order set to {@link ByteOrder#nativeOrder()}.
+ * Equivalent to the following code:
+ * {@snippet lang=java :
+ * JAVA_FLOAT.withBitAlignment(8);
+ * }
+ * @apiNote Care should be taken when using unaligned value layouts as they may induce
+ * performance and portability issues.
+ */
+ OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8);
+
+ /**
+ * An unaligned value layout constant whose size is the same as that of a Java {@code double}
+ * and byte order set to {@link ByteOrder#nativeOrder()}.
+ * Equivalent to the following code:
+ * {@snippet lang=java :
+ * JAVA_DOUBLE.withBitAlignment(8);
+ * }
+ * @apiNote Care should be taken when using unaligned value layouts as they may induce
+ * performance and portability issues.
+ */
+ OfDouble JAVA_DOUBLE_UNALIGNED = JAVA_DOUBLE.withBitAlignment(8);
+
}
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 87cfc200735b6..020661b1c945f 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
@@ -31,29 +31,36 @@
*
*
* The main abstraction introduced to support foreign memory access is {@link java.lang.foreign.MemorySegment}, which
- * models a contiguous memory region, residing either inside or outside the Java heap. The contents of a memory
+ * 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 dereference memory segments
- * using {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) access var handles},
+ * 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 memory region 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:
+ * 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, MemorySession.openImplicit());
+ * {@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) dereference method}
- * accepts a {@linkplain java.lang.foreign.ValueLayout value layout}, which specifies the size, alignment constraints,
- * byte order as well as the Java type ({@code int}, in this case) associated with the dereference operation. More specifically,
+ * {@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}.
@@ -64,35 +71,37 @@
* 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 associated
- * with a memory segment. This can be done, using the {@link java.lang.foreign.MemorySession} abstraction, as shown below:
+ * 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 (MemorySession session = MemorySession.openConfined()) {
- * MemorySegment segment = MemorySegment.allocateNative(10 * 4, session);
+ * {@snippet lang = java:
+ * try (Arena arena = Arena.openConfined()) {
+ * 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 a so called memory session,
- * which is used to bind the life-cycle of the segment created immediately afterwards. Note the use of the
- * try-with-resources construct: this idiom ensures that all the memory resources associated with the 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.
+ * 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,
* 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 dereference operation. We call this guarantee spatial safety;
+ * 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 can be closed (see above), segments are also validated (upon access) to make sure that
- * the memory session associated with the segment being accessed has not been closed prematurely.
+ * 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 memory location - or fails.
+ * 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},
@@ -105,134 +114,118 @@
* 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 :
+ * {@snippet lang = java:
* Linker linker = Linker.nativeLinker();
* SymbolLookup stdlib = linker.defaultLookup();
* MethodHandle strlen = linker.downcallHandle(
- * stdlib.lookup("strlen").get(),
+ * stdlib.find("strlen").get(),
* FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
* );
*
- * try (MemorySession session = MemorySession.openConfined()) {
- * MemorySegment cString = MemorySegment.allocateNative(5 + 1, session);
- * cString.setUtf8String(0, "Hello");
+ * try (Arena arena = Arena.openConfined()) {
+ * MemorySegment cString = arena.allocateUtf8String("Hello");
* long len = (long)strlen.invoke(cString); // 5
* }
- * }
+ *}
*
* Here, we obtain a {@linkplain java.lang.foreign.Linker#nativeLinker() native linker} and we use it
- * to {@linkplain java.lang.foreign.SymbolLookup#lookup(java.lang.String) look up} the {@code strlen} symbol in the
+ * 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
- * {@linkplain java.lang.foreign.Linker#downcallHandle(java.lang.foreign.FunctionDescriptor) obtained}.
+ * {@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...)})
* into a foreign function call, according to the rules specified by the ABI of the underlying platform.
- * The {@link java.lang.foreign.MemorySegment} class also provides many useful methods for
- * interacting with foreign code, such as converting Java strings
- * {@linkplain java.lang.foreign.MemorySegment#setUtf8String(long, java.lang.String) into} zero-terminated, UTF-8 strings and
- * {@linkplain java.lang.foreign.MemorySegment#getUtf8String(long) back}, as demonstrated in the above example.
- *
- *
Foreign addresses
- *
- * When a memory segment is created from Java code, the segment properties (spatial bounds, temporal bounds and confinement)
- * are fully known at segment creation. But when interacting with foreign functions, clients will often receive raw pointers.
- * Such pointers have no spatial bounds. For example, the C type {@code char*} can refer to a single {@code char} value,
- * or an array of {@code char} values, of given size. Nor do said pointers have any notion of temporal bounds or thread-confinement.
- *
- * Raw pointers are modelled using the {@link java.lang.foreign.MemoryAddress} class. When clients receive a
- * memory address instance from a foreign function call, they can perform memory dereference on it directly,
- * using one of the many unsafe
- * {@linkplain java.lang.foreign.MemoryAddress#get(java.lang.foreign.ValueLayout.OfInt, long) dereference methods}
- * provided:
- *
- * {@snippet lang=java :
- * MemoryAddress addr = ... // obtain address from foreign function call
- * int x = addr.get(ValueLayout.JAVA_INT, 0);
- * }
- *
- * Alternatively, the client can
- * {@linkplain java.lang.foreign.MemorySegment#ofAddress(java.lang.foreign.MemoryAddress, long, java.lang.foreign.MemorySession) create}
- * a memory segment unsafely. This allows the client to inject extra knowledge about spatial bounds which might,
- * for instance, be available in the documentation of the foreign function which produced the native address.
- * Here is how an unsafe segment can be created from a memory address:
- *
- * {@snippet lang=java :
- * MemorySession session = ... // initialize a memory session object
- * MemoryAddress addr = ... // obtain address from foreign function call
- * MemorySegment segment = MemorySegment.ofAddress(addr, 4, session); // segment is 4 bytes long
- * int x = segment.get(ValueLayout.JAVA_INT, 0);
- * }
+ * 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 address, so that Java code can effectively be passed to other foreign functions.
+ * 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(MemoryAddress addr1, MemoryAddress addr2) {
- * return addr1.get(ValueLayout.JAVA_INT, 0) - addr2.get(ValueLayout.JAVA_INT, 0);
+ * static int intCompare(MemorySegment addr1, MemorySegment addr2) {
+ * return addr1.get(ValueLayout.JAVA_INT, 0) -
+ * addr2.get(ValueLayout.JAVA_INT, 0);
+ *
* }
* }
* }
*
- * The above method dereferences two memory addresses containing an integer value, and performs a simple comparison
+ * 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, ValueLayout.ADDRESS);
+ * {@snippet lang = java:
+ * FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ * ValueLayout.ADDRESS.asUnbounded(),
+ * ValueLayout.ADDRESS.asUnbounded());
* MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class,
* "intCompare",
- * Linker.upcallType(comparFunction));
- * }
+ * 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.Linker#upcallType(java.lang.foreign.FunctionDescriptor) derive} a method type
+ * {@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 :
- * MemorySession session = ...
- * Addressable comparFunc = Linker.nativeLinker().upcallStub(
- * intCompareHandle, intCompareDescriptor, session);
+ * {@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, java.lang.foreign.FunctionDescriptor, java.lang.foreign.MemorySession) create}
+ * {@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.MemorySession memory session}
- * provided when the upcall stub is created. This same session is made available by the {@link java.lang.foreign.MemorySegment}
+ * 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(MemoryAddress, long, MemorySession)}
+ * 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.
*
- * 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(MemoryAddress, long, MemorySession)},
- * 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 dereference said segment. For these reasons, it is crucial for code that calls a restricted method
+ * 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.
*
- * Access to restricted methods can be controlled using the command line option {@code --enable-native-access=M1,M2, ... Mn},
- * where {@code M1}, {@code M2}, {@code ... Mn} are module names (for the unnamed module, the special value {@code ALL-UNNAMED}
- * can be used). If this option is specified, access to restricted methods is only granted to the modules listed by that
- * option. If this option is not specified, access to restricted methods is enabled for all modules, but
- * access to restricted methods will result in runtime warnings.
+ * 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
+ * to selected modules. This can be done either via implementation-specific command line options, or programmatically, e.g. by calling
+ * {@link java.lang.ModuleLayer.Controller#enableNativeAccess(java.lang.Module)}.
*
* For every class in this package, unless specified otherwise, any method arguments of reference
* type must not be null, and any null argument will elicit a {@code NullPointerException}. This fact is not individually
* documented for methods of this API.
+ *
+ * @apiNote Usual memory model guarantees, for example stated in {@jls 6.6} and {@jls 10.4}, do not apply
+ * when accessing native memory segments as these segments are backed by off-heap regions of memory.
+ *
+ * @implNote
+ * In the reference implementation, access to restricted methods can be granted to specific modules using the command line option
+ * {@code --enable-native-access=M1,M2, ... Mn}, where {@code M1}, {@code M2}, {@code ... Mn} are module names
+ * (for the unnamed module, the special value {@code ALL-UNNAMED} can be used). If this option is specified, access to
+ * restricted methods is only granted to the modules listed by that option. If this option is not specified,
+ * access to restricted methods is enabled for all modules, but access to restricted methods will result in runtime warnings.
+ *
*/
package java.lang.foreign;
+
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
index 28e7b3e8dec02..a63f53d3ca1db 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
@@ -45,8 +45,8 @@
import java.lang.constant.ConstantDescs;
import java.lang.foreign.GroupLayout;
-import java.lang.foreign.MemoryAddress;
import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.LambdaForm.BasicType;
import java.lang.reflect.Constructor;
@@ -7910,21 +7910,21 @@ private static MethodType tableSwitchChecks(MethodHandle defaultCase, MethodHand
* access modes {@code get} and {@code set} for {@code long} and
* {@code double} on 32-bit platforms.
*
atomic update access modes for {@code int}, {@code long},
- * {@code float}, {@code double} or {@link MemoryAddress}.
+ * {@code float}, {@code double} or {@link MemorySegment}.
* (Future major platform releases of the JDK may support additional
* types for certain currently unsupported access modes.)
- *
numeric atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}.
+ *
numeric atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}.
* (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.)
- *
bitwise atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}.
+ *
bitwise atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}.
* (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.)
*
*
- * If {@code T} is {@code float}, {@code double} or {@link MemoryAddress} then atomic
+ * If {@code T} is {@code float}, {@code double} or {@link MemorySegment} then atomic
* update access modes compare values using their bitwise representation
* (see {@link Float#floatToRawIntBits},
- * {@link Double#doubleToRawLongBits} and {@link MemoryAddress#toRawLongValue()}, respectively).
+ * {@link Double#doubleToRawLongBits} and {@link MemorySegment#address()}, respectively).
*
* Alternatively, a memory access operation is partially aligned if it occurs at a memory address {@code A}
* which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the
diff --git a/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java
index 176ee7db4595c..996b51487c9f8 100644
--- a/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java
+++ b/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java
@@ -52,24 +52,32 @@ private NativeMethodHandle(MethodType type, LambdaForm form, NativeEntryPoint ne
*/
public static MethodHandle make(NativeEntryPoint nep) {
MethodType type = nep.type();
- if (!allTypesPrimitive(type))
- throw new IllegalArgumentException("Type must only contain primitives: " + type);
+ if (hasIllegalType(type))
+ throw new IllegalArgumentException("Illegal type(s) found: " + type);
LambdaForm lform = preparedLambdaForm(type);
return new NativeMethodHandle(type, lform, nep);
}
- private static boolean allTypesPrimitive(MethodType type) {
- if (!type.returnType().isPrimitive())
- return false;
+ private static boolean hasIllegalType(MethodType type) {
+ if (isIllegalType(type.returnType()))
+ return true;
for (Class> pType : type.parameterArray()) {
- if (!pType.isPrimitive())
- return false;
+ if (isIllegalType(pType))
+ return true;
}
- return true;
+ return false;
+ }
+
+ private static boolean isIllegalType(Class> pType) {
+ return !(pType == long.class
+ || pType == int.class
+ || pType == float.class
+ || pType == double.class
+ || pType == void.class);
}
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template
index 05923554f19c0..08495f2e58d42 100644
--- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template
+++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template
@@ -34,7 +34,6 @@ import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.util.List;
diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
index 1f1af9a6fd5a1..b65306f6e21eb 100644
--- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
+++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
@@ -29,7 +29,6 @@ package java.nio;
import java.io.FileDescriptor;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.foreign.MemorySessionImpl;
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 b4a4a19502337..9ec890dcac08c 100644
--- a/src/java.base/share/classes/java/nio/channels/FileChannel.java
+++ b/src/java.base/share/classes/java/nio/channels/FileChannel.java
@@ -27,7 +27,7 @@
import java.io.IOException;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.spi.AbstractInterruptibleChannel;
@@ -1001,6 +1001,9 @@ public abstract MappedByteBuffer map(MapMode mode, long position, long size)
/**
* Maps a region of this channel's file into a new mapped memory segment,
* with the given offset, size and memory session.
+ * The {@linkplain MemorySegment#address() address} of the returned memory
+ * segment is the starting address of the mapped off-heap region backing
+ * the segment.
*
*
If the specified mapping mode is
* {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, the resulting
@@ -1053,11 +1056,11 @@ public abstract MappedByteBuffer map(MapMode mode, long position, long size)
*
* @throws IllegalStateException
* If the {@code session} is not
- * {@linkplain MemorySession#isAlive() alive}.
+ * {@linkplain SegmentScope#isAlive() alive}.
*
* @throws WrongThreadException
* If this method is called from a thread other than the thread
- * {@linkplain MemorySession#ownerThread() owning} the
+ * {@linkplain SegmentScope#isAccessibleBy(Thread) owning} the
* {@code session}.
*
* @throws NonReadableChannelException
@@ -1080,7 +1083,7 @@ public abstract MappedByteBuffer map(MapMode mode, long position, long size)
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
- public MemorySegment map(MapMode mode, long offset, long size, MemorySession session)
+ public MemorySegment map(MapMode mode, long offset, long size, SegmentScope session)
throws IOException
{
throw new UnsupportedOperationException();
diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
index bc1aeedc7af43..be8ee9d91f693 100644
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
@@ -270,12 +270,13 @@ public interface JavaLangAccess {
/**
* Updates all unnamed modules to allow access to restricted methods.
*/
- void addEnableNativeAccessAllUnnamed();
+ void addEnableNativeAccessToAllUnnamed();
/**
- * Returns true if module m can access restricted methods.
+ * Ensure that the given module has native access. If not, warn or
+ * throw exception depending on the configuration.
*/
- boolean isEnableNativeAccess(Module m);
+ void ensureNativeAccess(Module m, Class> owner, String methodName);
/**
* Returns the ServicesCatalog for the given Layer.
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 3bfa39cc9b983..9080e1482a84e 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
@@ -25,12 +25,12 @@
package jdk.internal.foreign;
-import java.lang.foreign.MemoryAddress;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.foreign.SegmentAllocator;
+import java.lang.foreign.SegmentScope;
import java.lang.foreign.ValueLayout;
+import java.lang.reflect.Array;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -51,6 +51,7 @@
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.ScopedMemoryAccess;
+import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
@@ -66,26 +67,26 @@
* are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and
* {@link MappedMemorySegmentImpl}.
*/
-public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegment, SegmentAllocator, Scoped, BiFunction, RuntimeException> {
+public abstract sealed class AbstractMemorySegmentImpl
+ implements MemorySegment, SegmentAllocator, BiFunction, RuntimeException>
+ permits HeapMemorySegmentImpl, NativeMemorySegmentImpl {
private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
- static final long NONCE = new Random().nextLong();
-
- static final JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
+ static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
final long length;
final boolean readOnly;
- final MemorySession session;
+ final SegmentScope session;
@ForceInline
- AbstractMemorySegmentImpl(long length, boolean readOnly, MemorySession session) {
+ AbstractMemorySegmentImpl(long length, boolean readOnly, SegmentScope session) {
this.length = length;
this.readOnly = readOnly;
this.session = session;
}
- abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session);
+ abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope session);
abstract ByteBuffer makeByteBuffer();
@@ -125,7 +126,7 @@ public Spliterator spliterator(MemoryLayout elementLayout) {
if (!isAlignedForElement(0, elementLayout)) {
throw new IllegalArgumentException("Incompatible alignment constraints");
}
- if (!Utils.isAligned(byteSize(), elementLayout.byteSize())) {
+ if ((byteSize() % elementLayout.byteSize()) != 0) {
throw new IllegalArgumentException("Segment size is not a multiple of layout size");
}
return new SegmentSplitter(elementLayout.byteSize(), byteSize() / elementLayout.byteSize(),
@@ -145,52 +146,15 @@ public final MemorySegment fill(byte value){
}
@Override
- public MemorySegment allocate(long bytesSize, long bytesAlignment) {
- Utils.checkAllocationSizeAndAlign(bytesSize, bytesAlignment);
- return asSlice(0, bytesSize);
- }
-
- @Override
- public long mismatch(MemorySegment other) {
- AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other);
- final long thisSize = this.byteSize();
- final long thatSize = that.byteSize();
- final long length = Math.min(thisSize, thatSize);
- this.checkAccess(0, length, true);
- that.checkAccess(0, length, true);
- if (this == other) {
- checkValidState();
- return -1;
- }
-
- long i = 0;
- if (length > 7) {
- if (get(JAVA_BYTE, 0) != that.get(JAVA_BYTE, 0)) {
- return 0;
- }
- i = vectorizedMismatchLargeForBytes(sessionImpl(), that.sessionImpl(),
- this.unsafeGetBase(), this.unsafeGetOffset(),
- that.unsafeGetBase(), that.unsafeGetOffset(),
- length);
- if (i >= 0) {
- return i;
- }
- long remaining = ~i;
- assert remaining < 8 : "remaining greater than 7: " + remaining;
- i = length - remaining;
- }
- for (; i < length; i++) {
- if (get(JAVA_BYTE, i) != that.get(JAVA_BYTE, i)) {
- return i;
- }
- }
- return thisSize != thatSize ? length : -1;
+ public MemorySegment allocate(long byteSize, long byteAlignment) {
+ Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
+ return asSlice(0, byteSize);
}
/**
* Mismatch over long lengths.
*/
- private static long vectorizedMismatchLargeForBytes(MemorySessionImpl aSession, MemorySessionImpl bSession,
+ public static long vectorizedMismatchLargeForBytes(MemorySessionImpl aSession, MemorySessionImpl bSession,
Object a, long aOffset,
Object b, long bOffset,
long length) {
@@ -219,11 +183,6 @@ private static long vectorizedMismatchLargeForBytes(MemorySessionImpl aSession,
return ~remaining;
}
- @Override
- public MemoryAddress address() {
- throw new UnsupportedOperationException("Cannot obtain address of on-heap segment");
- }
-
@Override
public final ByteBuffer asByteBuffer() {
checkArraySize("ByteBuffer", 1);
@@ -279,21 +238,25 @@ public final long segmentOffset(MemorySegment other) {
@Override
public void load() {
- throw new UnsupportedOperationException("Not a mapped segment");
+ throw notAMappedSegment();
}
@Override
public void unload() {
- throw new UnsupportedOperationException("Not a mapped segment");
+ throw notAMappedSegment();
}
@Override
public boolean isLoaded() {
- throw new UnsupportedOperationException("Not a mapped segment");
+ throw notAMappedSegment();
}
@Override
public void force() {
+ throw notAMappedSegment();
+ }
+
+ private static UnsupportedOperationException notAMappedSegment() {
throw new UnsupportedOperationException("Not a mapped segment");
}
@@ -366,6 +329,7 @@ public final boolean isAlignedForElement(long offset, MemoryLayout layout) {
}
private int checkArraySize(String typeName, int elemSize) {
+ // elemSize is guaranteed to be a power of two, so we can use an alignment check
if (!Utils.isAligned(length, elemSize)) {
throw new IllegalStateException(String.format("Segment size is not a multiple of %d. Size: %d", elemSize, length));
}
@@ -394,20 +358,20 @@ public RuntimeException apply(String s, List numbers) {
}
@Override
- public MemorySession session() {
+ public SegmentScope scope() {
return session;
}
+ @ForceInline
+ public final MemorySessionImpl sessionImpl() {
+ return (MemorySessionImpl)session;
+ }
+
private IndexOutOfBoundsException outOfBoundException(long offset, long length) {
return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
this, offset, length));
}
- protected int id() {
- //compute a stable and random id for this memory segment
- return Math.abs(Objects.hash(unsafeGetBase(), unsafeGetOffset(), NONCE));
- }
-
static class SegmentSplitter implements Spliterator {
AbstractMemorySegmentImpl segment;
long elemCount;
@@ -486,42 +450,38 @@ public int characteristics() {
@Override
public String toString() {
- return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + length + " }";
+ return "MemorySegment{ array: " + array() + " address:" + address() + " limit: " + length + " }";
}
@Override
public boolean equals(Object o) {
return o instanceof AbstractMemorySegmentImpl that &&
- isNative() == that.isNative() &&
- unsafeGetOffset() == that.unsafeGetOffset() &&
unsafeGetBase() == that.unsafeGetBase() &&
- length == that.length &&
- session.equals(that.session);
+ unsafeGetOffset() == that.unsafeGetOffset();
}
@Override
public int hashCode() {
return Objects.hash(
- isNative(),
unsafeGetOffset(),
- unsafeGetBase(),
- length,
- session
- );
+ unsafeGetBase());
}
public static AbstractMemorySegmentImpl ofBuffer(Buffer bb) {
Objects.requireNonNull(bb);
- long bbAddress = nioAccess.getBufferAddress(bb);
- Object base = nioAccess.getBufferBase(bb);
- UnmapperProxy unmapper = nioAccess.unmapper(bb);
+ Object base = NIO_ACCESS.getBufferBase(bb);
+ if (!bb.isDirect() && base == null) {
+ throw new IllegalArgumentException("The provided heap buffer is not backed by an array.");
+ }
+ long bbAddress = NIO_ACCESS.getBufferAddress(bb);
+ UnmapperProxy unmapper = NIO_ACCESS.unmapper(bb);
int pos = bb.position();
int limit = bb.limit();
int size = limit - pos;
- AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb);
- final MemorySession bufferSession;
+ AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl) NIO_ACCESS.bufferSegment(bb);
+ final SegmentScope bufferSession;
if (bufferSegment != null) {
bufferSession = bufferSegment.session;
} else {
@@ -574,4 +534,152 @@ private static int getScaleFactor(Buffer buffer) {
throw new AssertionError("Cannot get here");
}
}
+
+ @ForceInline
+ public static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long srcOffset,
+ MemorySegment dstSegment, ValueLayout dstElementLayout, long dstOffset,
+ long elementCount) {
+
+ 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());
+ }
+ }
+
+ @ForceInline
+ public static void copy(MemorySegment srcSegment, ValueLayout srcLayout, long srcOffset,
+ Object dstArray, int dstIndex,
+ int elementCount) {
+
+ long baseAndScale = getBaseAndScale(dstArray.getClass());
+ if (dstArray.getClass().componentType() != srcLayout.carrier()) {
+ throw new IllegalArgumentException("Incompatible value layout: " + srcLayout);
+ }
+ int dstBase = (int)baseAndScale;
+ long dstWidth = (int)(baseAndScale >> 32); // Use long arithmetics below
+ AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment;
+ Utils.checkElementAlignment(srcLayout, "Source layout alignment greater than its size");
+ if (!srcImpl.isAlignedForElement(srcOffset, srcLayout)) {
+ throw new IllegalArgumentException("Source segment incompatible with alignment constraints");
+ }
+ srcImpl.checkAccess(srcOffset, elementCount * dstWidth, true);
+ Objects.checkFromIndexSize(dstIndex, elementCount, Array.getLength(dstArray));
+ if (dstWidth == 1 || srcLayout.order() == ByteOrder.nativeOrder()) {
+ ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(srcImpl.sessionImpl(), null,
+ srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset,
+ dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth);
+ } else {
+ ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.sessionImpl(), null,
+ srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset,
+ dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth, dstWidth);
+ }
+ }
+
+ @ForceInline
+ public static void copy(Object srcArray, int srcIndex,
+ MemorySegment dstSegment, ValueLayout dstLayout, long dstOffset,
+ int elementCount) {
+
+ long baseAndScale = getBaseAndScale(srcArray.getClass());
+ if (srcArray.getClass().componentType() != dstLayout.carrier()) {
+ throw new IllegalArgumentException("Incompatible value layout: " + dstLayout);
+ }
+ int srcBase = (int)baseAndScale;
+ long srcWidth = (int)(baseAndScale >> 32); // Use long arithmetics below
+ Objects.checkFromIndexSize(srcIndex, elementCount, Array.getLength(srcArray));
+ AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dstSegment;
+ Utils.checkElementAlignment(dstLayout, "Destination layout alignment greater than its size");
+ if (!destImpl.isAlignedForElement(dstOffset, dstLayout)) {
+ throw new IllegalArgumentException("Destination segment incompatible with alignment constraints");
+ }
+ destImpl.checkAccess(dstOffset, elementCount * srcWidth, false);
+ if (srcWidth == 1 || dstLayout.order() == ByteOrder.nativeOrder()) {
+ ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(null, destImpl.sessionImpl(),
+ srcArray, srcBase + (srcIndex * srcWidth),
+ destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth);
+ } else {
+ ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(null, destImpl.sessionImpl(),
+ srcArray, srcBase + (srcIndex * srcWidth),
+ destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth, srcWidth);
+ }
+ }
+
+ public static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOffset,
+ MemorySegment dstSegment, long dstFromOffset, long dstToOffset) {
+ AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)Objects.requireNonNull(srcSegment);
+ AbstractMemorySegmentImpl dstImpl = (AbstractMemorySegmentImpl)Objects.requireNonNull(dstSegment);
+ long srcBytes = srcToOffset - srcFromOffset;
+ long dstBytes = dstToOffset - dstFromOffset;
+ srcImpl.checkAccess(srcFromOffset, srcBytes, true);
+ dstImpl.checkAccess(dstFromOffset, dstBytes, true);
+ if (dstImpl == srcImpl) {
+ srcImpl.checkValidState();
+ return -1;
+ }
+
+ long bytes = Math.min(srcBytes, dstBytes);
+ long i = 0;
+ if (bytes > 7) {
+ if (srcImpl.get(JAVA_BYTE, srcFromOffset) != dstImpl.get(JAVA_BYTE, dstFromOffset)) {
+ return 0;
+ }
+ i = AbstractMemorySegmentImpl.vectorizedMismatchLargeForBytes(srcImpl.sessionImpl(), dstImpl.sessionImpl(),
+ srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcFromOffset,
+ dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstFromOffset,
+ bytes);
+ if (i >= 0) {
+ return i;
+ }
+ long remaining = ~i;
+ assert remaining < 8 : "remaining greater than 7: " + remaining;
+ i = bytes - remaining;
+ }
+ for (; i < bytes; i++) {
+ if (srcImpl.get(JAVA_BYTE, srcFromOffset + i) != dstImpl.get(JAVA_BYTE, dstFromOffset + i)) {
+ return i;
+ }
+ }
+ return srcBytes != dstBytes ? bytes : -1;
+ }
+
+ private static long getBaseAndScale(Class> arrayType) {
+ if (arrayType.equals(byte[].class)) {
+ return (long) Unsafe.ARRAY_BYTE_BASE_OFFSET | ((long)Unsafe.ARRAY_BYTE_INDEX_SCALE << 32);
+ } else if (arrayType.equals(char[].class)) {
+ return (long) Unsafe.ARRAY_CHAR_BASE_OFFSET | ((long)Unsafe.ARRAY_CHAR_INDEX_SCALE << 32);
+ } else if (arrayType.equals(short[].class)) {
+ return (long)Unsafe.ARRAY_SHORT_BASE_OFFSET | ((long)Unsafe.ARRAY_SHORT_INDEX_SCALE << 32);
+ } else if (arrayType.equals(int[].class)) {
+ return (long)Unsafe.ARRAY_INT_BASE_OFFSET | ((long) Unsafe.ARRAY_INT_INDEX_SCALE << 32);
+ } else if (arrayType.equals(float[].class)) {
+ return (long)Unsafe.ARRAY_FLOAT_BASE_OFFSET | ((long)Unsafe.ARRAY_FLOAT_INDEX_SCALE << 32);
+ } else if (arrayType.equals(long[].class)) {
+ return (long)Unsafe.ARRAY_LONG_BASE_OFFSET | ((long)Unsafe.ARRAY_LONG_INDEX_SCALE << 32);
+ } else if (arrayType.equals(double[].class)) {
+ return (long)Unsafe.ARRAY_DOUBLE_BASE_OFFSET | ((long)Unsafe.ARRAY_DOUBLE_INDEX_SCALE << 32);
+ } else {
+ throw new IllegalArgumentException("Not a supported array class: " + arrayType.getSimpleName());
+ }
+ }
}
diff --git a/src/java.base/share/classes/jdk/internal/foreign/ArenaAllocator.java b/src/java.base/share/classes/jdk/internal/foreign/ArenaAllocator.java
deleted file mode 100644
index 0ae38e3dc398d..0000000000000
--- a/src/java.base/share/classes/jdk/internal/foreign/ArenaAllocator.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. 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;
-
-import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
-import java.lang.foreign.SegmentAllocator;
-
-public final class ArenaAllocator implements SegmentAllocator {
-
- public static final long DEFAULT_BLOCK_SIZE = 4 * 1024;
-
- private MemorySegment segment;
-
- private long sp = 0L;
- private long size = 0;
- private final long blockSize;
- private final long arenaSize;
- private final MemorySession session;
-
- public ArenaAllocator(long blockSize, long arenaSize, MemorySession session) {
- this.blockSize = blockSize;
- this.arenaSize = arenaSize;
- this.session = session;
- this.segment = newSegment(blockSize, 1);
- }
-
- MemorySegment trySlice(long bytesSize, long bytesAlignment) {
- long min = segment.address().toRawLongValue();
- long start = Utils.alignUp(min + sp, bytesAlignment) - min;
- if (segment.byteSize() - start < bytesSize) {
- return null;
- } else {
- MemorySegment slice = segment.asSlice(start, bytesSize);
- sp = start + bytesSize;
- return slice;
- }
- }
-
- private MemorySegment newSegment(long bytesSize, long bytesAlignment) {
- long allocatedSize = Utils.alignUp(bytesSize, bytesAlignment);
- if (size + allocatedSize > arenaSize) {
- throw new OutOfMemoryError();
- }
- size += allocatedSize;
- return MemorySegment.allocateNative(bytesSize, bytesAlignment, session);
- }
-
- @Override
- public MemorySegment allocate(long bytesSize, long bytesAlignment) {
- Utils.checkAllocationSizeAndAlign(bytesSize, bytesAlignment);
- // try to slice from current segment first...
- MemorySegment slice = trySlice(bytesSize, bytesAlignment);
- if (slice != null) {
- return slice;
- } else {
- long maxPossibleAllocationSize = bytesSize + bytesAlignment - 1;
- if (maxPossibleAllocationSize < 0) {
- throw new OutOfMemoryError();
- } else if (maxPossibleAllocationSize > blockSize) {
- // too big
- return newSegment(bytesSize, bytesAlignment);
- } else {
- // allocate a new segment and slice from there
- sp = 0L;
- segment = newSegment(blockSize, 1L);
- slice = trySlice(bytesSize, bytesAlignment);
- return slice;
- }
- }
- }
-}
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 4b4d95149f863..59d5e1e42c83a 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/CABI.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/CABI.java
@@ -29,10 +29,10 @@
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
public enum CABI {
- SysV,
- Win64,
- LinuxAArch64,
- MacOsAArch64;
+ SYS_V,
+ WIN_64,
+ LINUX_AARCH_64,
+ MAC_OS_AARCH_64;
private static final CABI ABI;
private static final String ARCH;
@@ -47,16 +47,16 @@ public enum CABI {
// addressSize will be correctly 32
if ((ARCH.equals("amd64") || ARCH.equals("x86_64")) && ADDRESS_SIZE == 64) {
if (OS.startsWith("Windows")) {
- ABI = Win64;
+ ABI = WIN_64;
} else {
- ABI = SysV;
+ ABI = SYS_V;
}
} else if (ARCH.equals("aarch64")) {
if (OS.startsWith("Mac")) {
- ABI = MacOsAArch64;
+ ABI = MAC_OS_AARCH_64;
} else {
// The Linux ABI follows the standard AAPCS ABI
- ABI = LinuxAArch64;
+ ABI = LINUX_AARCH_64;
}
} else {
// unsupported
diff --git a/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java b/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java
index b5ef673b92b15..4961cecde5e1c 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java
@@ -51,8 +51,8 @@ final class ConfinedSession extends MemorySessionImpl {
}
}
- public ConfinedSession(Thread owner, Cleaner cleaner) {
- super(owner, new ConfinedResourceList(), cleaner);
+ public ConfinedSession(Thread owner) {
+ super(owner, new ConfinedResourceList());
}
@Override
@@ -81,10 +81,12 @@ public void release0() {
void justClose() {
checkValidState();
- if (state == 0 || state - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) {
+ int asyncCount = (int)ASYNC_RELEASE_COUNT.getVolatile(this);
+ if ((state == 0 && asyncCount == 0)
+ || ((state - asyncCount) == 0)) {
state = CLOSED;
} else {
- throw alreadyAcquired(state);
+ throw alreadyAcquired(state - asyncCount);
}
}
diff --git a/src/java.base/share/classes/jdk/internal/foreign/FunctionDescriptorImpl.java b/src/java.base/share/classes/jdk/internal/foreign/FunctionDescriptorImpl.java
new file mode 100644
index 0000000000000..0723af6ba0def
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/FunctionDescriptorImpl.java
@@ -0,0 +1,186 @@
+/*
+ * 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. 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;
+
+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.MethodType;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * @implSpec This class and its subclasses are immutable, thread-safe and value-based.
+ */
+public final class FunctionDescriptorImpl implements FunctionDescriptor {
+
+ private final MemoryLayout resLayout; // Nullable
+ private final List argLayouts;
+
+ private FunctionDescriptorImpl(MemoryLayout resLayout, List argLayouts) {
+ this.resLayout = resLayout;
+ this.argLayouts = List.copyOf(argLayouts);
+ }
+
+ /**
+ * {@return the return layout (if any) associated with this function descriptor}
+ */
+ public Optional returnLayout() {
+ return Optional.ofNullable(resLayout);
+ }
+
+ /**
+ * {@return the argument layouts associated with this function descriptor (as an immutable list)}.
+ */
+ public List argumentLayouts() {
+ return argLayouts;
+ }
+
+ /**
+ * Returns a function descriptor with the given argument layouts appended to the argument layout array
+ * of this function descriptor.
+ *
+ * @param addedLayouts the argument layouts to append.
+ * @return the new function descriptor.
+ */
+ public FunctionDescriptorImpl appendArgumentLayouts(MemoryLayout... addedLayouts) {
+ return insertArgumentLayouts(argLayouts.size(), addedLayouts);
+ }
+
+ /**
+ * Returns a function descriptor with the given argument layouts inserted at the given index, into the argument
+ * layout array of this function descriptor.
+ *
+ * @param index the index at which to insert the arguments
+ * @param addedLayouts the argument layouts to insert at given index.
+ * @return the new function descriptor.
+ * @throws IllegalArgumentException if {@code index < 0 || index > argumentLayouts().size()}.
+ */
+ public FunctionDescriptorImpl insertArgumentLayouts(int index, MemoryLayout... addedLayouts) {
+ if (index < 0 || index > argLayouts.size())
+ throw new IllegalArgumentException("Index out of bounds: " + index);
+ List added = List.of(addedLayouts); // null check on array and its elements
+ List newLayouts = new ArrayList<>(argLayouts.size() + addedLayouts.length);
+ newLayouts.addAll(argLayouts.subList(0, index));
+ newLayouts.addAll(added);
+ newLayouts.addAll(argLayouts.subList(index, argLayouts.size()));
+ return new FunctionDescriptorImpl(resLayout, newLayouts);
+ }
+
+ /**
+ * Returns a function descriptor with the given memory layout as the new return layout.
+ *
+ * @param newReturn the new return layout.
+ * @return the new function descriptor.
+ */
+ public FunctionDescriptorImpl changeReturnLayout(MemoryLayout newReturn) {
+ requireNonNull(newReturn);
+ return new FunctionDescriptorImpl(newReturn, argLayouts);
+ }
+
+ /**
+ * Returns a function descriptor with the return layout dropped. This is useful to model functions
+ * which return no values.
+ *
+ * @return the new function descriptor.
+ */
+ public FunctionDescriptorImpl dropReturnLayout() {
+ return new FunctionDescriptorImpl(null, argLayouts);
+ }
+
+ private static Class> carrierTypeFor(MemoryLayout layout) {
+ if (layout instanceof ValueLayout valueLayout) {
+ return valueLayout.carrier();
+ } else if (layout instanceof GroupLayout) {
+ return MemorySegment.class;
+ } else {
+ throw new IllegalArgumentException("Unsupported layout: " + layout);
+ }
+ }
+
+ @Override
+ public MethodType toMethodType() {
+ Class> returnValue = resLayout != null ? carrierTypeFor(resLayout) : void.class;
+ Class>[] argCarriers = new Class>[argLayouts.size()];
+ for (int i = 0; i < argCarriers.length; i++) {
+ argCarriers[i] = carrierTypeFor(argLayouts.get(i));
+ }
+ return MethodType.methodType(returnValue, argCarriers);
+ }
+
+ /**
+ * {@return the string representation of this function descriptor}
+ */
+ @Override
+ public String toString() {
+ return String.format("(%s)%s",
+ argLayouts.stream().map(Object::toString)
+ .collect(Collectors.joining()),
+ returnLayout()
+ .map(Object::toString)
+ .orElse("v"));
+ }
+
+ /**
+ * Compares the specified object with this function descriptor for equality. Returns {@code true} if and only if the specified
+ * object is also a function descriptor, and all the following conditions are met:
+ *
+ *
the two function descriptors have equals return layouts (see {@link MemoryLayout#equals(Object)}), or both have no return layout;
+ *
the two function descriptors have argument layouts that are pair-wise {@linkplain MemoryLayout#equals(Object) equal}; and
+ *
+ *
+ * @param other the object to be compared for equality with this function descriptor.
+ * @return {@code true} if the specified object is equal to this function descriptor.
+ */
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof FunctionDescriptorImpl f &&
+ Objects.equals(resLayout, f.resLayout) &&
+ Objects.equals(argLayouts, f.argLayouts);
+ }
+
+ /**
+ * {@return the hash code value for this function descriptor}
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(argLayouts, resLayout);
+ }
+
+ public static FunctionDescriptor of(MemoryLayout resLayout, List argLayouts) {
+ return new FunctionDescriptorImpl(resLayout, argLayouts);
+ }
+
+ public static FunctionDescriptor ofVoid(List argLayouts) {
+ return new FunctionDescriptorImpl(null, argLayouts);
+ }
+}
diff --git a/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java b/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java
new file mode 100644
index 0000000000000..2d78a66eb6f8d
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java
@@ -0,0 +1,70 @@
+/*
+ * 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 jdk.internal.foreign;
+
+import jdk.internal.vm.annotation.ForceInline;
+
+/**
+ * The global, non-closeable, shared session. Similar to a shared session, but its {@link #close()} method throws unconditionally.
+ * Adding new resources to the global session, does nothing: as the session can never become not-alive, there is nothing to track.
+ * Acquiring and or releasing a memory session similarly does nothing.
+ */
+final class GlobalSession extends MemorySessionImpl {
+
+ final Object ref;
+
+ public GlobalSession(Object ref) {
+ super(null, null);
+ this.ref = ref;
+ }
+
+ @Override
+ @ForceInline
+ public void release0() {
+ // do nothing
+ }
+
+ @Override
+ public boolean isCloseable() {
+ return false;
+ }
+
+ @Override
+ @ForceInline
+ public void acquire0() {
+ // do nothing
+ }
+
+ @Override
+ void addInternal(ResourceList.ResourceCleanup resource) {
+ // do nothing
+ }
+
+ @Override
+ public void justClose() {
+ throw nonCloseable();
+ }
+}
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 3d7d05e7d40e5..4967f438c003c 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java
@@ -27,9 +27,11 @@
package jdk.internal.foreign;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.nio.ByteBuffer;
import java.util.Objects;
+import java.util.Optional;
+
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
@@ -44,9 +46,9 @@
* 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, int, MemorySession)}.
+ * such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, SegmentScope)}.
*/
-public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
+public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
@@ -59,9 +61,14 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
final long offset;
final Object base;
+ @Override
+ public Optional
*
two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal
- *
two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
- * {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
+ *
two group layouts are considered equal if they are of the same type (see {@link StructLayout},
+ * {@link UnionLayout}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
*
*
* @param other the object to be compared for equality with this layout.
@@ -154,14 +122,35 @@ public boolean equals(Object other) {
return true;
}
- return other instanceof AbstractLayout otherLayout &&
+ return other instanceof AbstractLayout> otherLayout &&
name.equals(otherLayout.name) &&
- size == otherLayout.size &&
- alignment == otherLayout.alignment;
+ bitSize == otherLayout.bitSize &&
+ bitAlignment == otherLayout.bitAlignment;
}
/**
* {@return the string representation of this layout}
*/
public abstract String toString();
+
+ abstract L dup(long alignment, Optional name);
+
+ String decorateLayoutString(String s) {
+ if (name().isPresent()) {
+ s = String.format("%s(%s)", s, name().get());
+ }
+ if (!hasNaturalAlignment()) {
+ 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);
+ }
+ }
+
+
}
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
new file mode 100644
index 0000000000000..fa154bd09babc
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019, 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 jdk.internal.foreign.layout;
+
+public final class MemoryLayoutUtil {
+
+ private MemoryLayoutUtil() {
+ }
+
+ public static void checkSize(long size) {
+ checkSize(size, false);
+ }
+
+ public static void checkSize(long size, boolean includeZero) {
+ if (size < 0 || (!includeZero && size == 0)) {
+ throw new IllegalArgumentException("Invalid size for layout: " + size);
+ }
+ }
+
+}
\ 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
new file mode 100644
index 0000000000000..3559a8d0f297d
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, 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 jdk.internal.foreign.layout;
+
+import java.lang.foreign.PaddingLayout;
+import java.util.Objects;
+import java.util.Optional;
+
+public final class PaddingLayoutImpl extends AbstractLayout implements PaddingLayout {
+
+ private PaddingLayoutImpl(long bitSize) {
+ this(bitSize, 1, Optional.empty());
+ }
+
+ private PaddingLayoutImpl(long bitSize, long bitAlignment, Optional name) {
+ super(bitSize, bitAlignment, name);
+ }
+
+ @Override
+ public String toString() {
+ return decorateLayoutString("x" + bitSize());
+ }
+
+ @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();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), bitSize());
+ }
+
+ @Override
+ PaddingLayoutImpl dup(long bitAlignment, Optional name) {
+ return new PaddingLayoutImpl(bitSize(), bitAlignment, name);
+ }
+
+ @Override
+ public boolean hasNaturalAlignment() {
+ return true;
+ }
+
+ public static PaddingLayout of(long bitSize) {
+ return new PaddingLayoutImpl(bitSize);
+ }
+
+}
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
new file mode 100644
index 0000000000000..c249f76f9ee2d
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2019, 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 jdk.internal.foreign.layout;
+
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.SequenceLayout;
+import java.util.Objects;
+import java.util.Optional;
+
+public final class SequenceLayoutImpl extends AbstractLayout implements SequenceLayout {
+
+ private final long elemCount;
+ private final MemoryLayout elementLayout;
+
+ private SequenceLayoutImpl(long elemCount, MemoryLayout elementLayout) {
+ this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
+ }
+
+ private SequenceLayoutImpl(long elemCount, MemoryLayout elementLayout, long bitAlignment, Optional name) {
+ super(Math.multiplyExact(elemCount, elementLayout.bitSize()), bitAlignment, name);
+ this.elemCount = elemCount;
+ this.elementLayout = elementLayout;
+ }
+
+ /**
+ * {@return the element layout associated with this sequence layout}
+ */
+ public MemoryLayout elementLayout() {
+ return elementLayout;
+ }
+
+ /**
+ * {@return the element count of this sequence layout}
+ */
+ public long elementCount() {
+ return elemCount;
+ }
+
+ /**
+ * Returns a sequence layout with the same element layout, alignment constraints and name as this sequence layout,
+ * but with the specified element count.
+ *
+ * @param elementCount the new element count.
+ * @return a sequence layout with the given element count.
+ * @throws IllegalArgumentException if {@code elementCount < 0}.
+ */
+ public SequenceLayout withElementCount(long elementCount) {
+ MemoryLayoutUtil.checkSize(elementCount, true);
+ return new SequenceLayoutImpl(elementCount, elementLayout, bitAlignment(), name());
+ }
+
+ /**
+ * Re-arrange the elements in this sequence layout into a multi-dimensional sequence layout.
+ * The resulting layout is a sequence layout where element layouts in the flattened projection of this
+ * sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts
+ * according to the provided element counts. This transformation preserves the layout size;
+ * that is, multiplying the provided element counts must yield the same element count
+ * as the flattened projection of this sequence layout.
+ *
+ * For instance, given a sequence layout of the kind:
+ * {@snippet lang = java:
+ * var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT));
+ *}
+ * calling {@code seq.reshape(2, 6)} will yield the following sequence layout:
+ * {@snippet lang = java:
+ * var reshapeSeq = MemoryLayout.sequenceLayout(2, MemoryLayout.sequenceLayout(6, ValueLayout.JAVA_INT));
+ *}
+ *
+ * If one of the provided element count is the special value {@code -1}, then the element
+ * count in that position will be inferred from the remaining element counts and the
+ * element count of the flattened projection of this layout. For instance, a layout equivalent to
+ * the above {@code reshapeSeq} can also be computed in the following ways:
+ * {@snippet lang = java:
+ * var reshapeSeqImplicit1 = seq.reshape(-1, 6);
+ * var reshapeSeqImplicit2 = seq.reshape(2, -1);
+ *}
+ *
+ * @param elementCounts an array of element counts, of which at most one can be {@code -1}.
+ * @return a sequence layout where element layouts in the flattened projection of this
+ * sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts.
+ * @throws IllegalArgumentException if two or more element counts are set to {@code -1}, or if one
+ * or more element count is {@code <= 0} (but other than {@code -1}) or, if, after any required inference,
+ * multiplying the element counts does not yield the same element count as the flattened projection of this
+ * sequence layout.
+ */
+ public SequenceLayout reshape(long... elementCounts) {
+ Objects.requireNonNull(elementCounts);
+ if (elementCounts.length == 0) {
+ throw new IllegalArgumentException();
+ }
+ SequenceLayout flat = flatten();
+ long expectedCount = flat.elementCount();
+
+ long actualCount = 1;
+ int inferPosition = -1;
+ for (int i = 0; i < elementCounts.length; i++) {
+ if (elementCounts[i] == -1) {
+ if (inferPosition == -1) {
+ inferPosition = i;
+ } else {
+ throw new IllegalArgumentException("Too many unspecified element counts");
+ }
+ } else if (elementCounts[i] <= 0) {
+ throw new IllegalArgumentException("Invalid element count: " + elementCounts[i]);
+ } else {
+ actualCount = elementCounts[i] * actualCount;
+ }
+ }
+
+ // infer an unspecified element count (if any)
+ if (inferPosition != -1) {
+ long inferredCount = expectedCount / actualCount;
+ elementCounts[inferPosition] = inferredCount;
+ actualCount = actualCount * inferredCount;
+ }
+
+ if (actualCount != expectedCount) {
+ throw new IllegalArgumentException("Element counts do not match expected size: " + expectedCount);
+ }
+
+ MemoryLayout res = flat.elementLayout();
+ for (int i = elementCounts.length - 1; i >= 0; i--) {
+ res = MemoryLayout.sequenceLayout(elementCounts[i], res);
+ }
+ return (SequenceLayoutImpl) res;
+ }
+
+ /**
+ * Returns a flattened sequence layout. The element layout of the returned sequence layout
+ * is the first non-sequence element layout found by recursively traversing the element layouts of this sequence layout.
+ * This transformation preserves the layout size; nested sequence layout in this sequence layout will
+ * be dropped and their element counts will be incorporated into that of the returned sequence layout.
+ * For instance, given a sequence layout of the kind:
+ * {@snippet lang = java:
+ * var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT));
+ *}
+ * calling {@code seq.flatten()} will yield the following sequence layout:
+ * {@snippet lang = java:
+ * var flattenedSeq = MemoryLayout.sequenceLayout(12, ValueLayout.JAVA_INT);
+ *}
+ *
+ * @return a sequence layout with the same size as this layout (but, possibly, with different
+ * element count), whose element layout is not a sequence layout.
+ */
+ public SequenceLayout flatten() {
+ long count = elementCount();
+ MemoryLayout elemLayout = elementLayout();
+ while (elemLayout instanceof SequenceLayoutImpl elemSeq) {
+ count = count * elemSeq.elementCount();
+ elemLayout = elemSeq.elementLayout();
+ }
+ return MemoryLayout.sequenceLayout(count, elemLayout);
+ }
+
+ @Override
+ public String toString() {
+ return decorateLayoutString(String.format("[%s:%s]",
+ 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);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), elemCount, elementLayout);
+ }
+
+ @Override
+ SequenceLayoutImpl dup(long bitAlignment, Optional name) {
+ return new SequenceLayoutImpl(elementCount(), elementLayout, bitAlignment, name);
+ }
+
+ @Override
+ public boolean hasNaturalAlignment() {
+ return bitAlignment() == elementLayout.bitAlignment();
+ }
+
+ public static SequenceLayout of(long elementCount, MemoryLayout elementLayout) {
+ return new SequenceLayoutImpl(elementCount, elementLayout);
+ }
+
+}
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
new file mode 100644
index 0000000000000..339afd735bbc3
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019, 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 jdk.internal.foreign.layout;
+
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.StructLayout;
+import java.util.List;
+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);
+ }
+
+ @Override
+ StructLayoutImpl dup(long bitAlignment, Optional name) {
+ return new StructLayoutImpl(memberLayouts(), bitAlignment, name);
+ }
+
+ public static StructLayout of(List elements) {
+ return new StructLayoutImpl(elements);
+ }
+
+}
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
new file mode 100644
index 0000000000000..d8adb186c62db
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019, 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 jdk.internal.foreign.layout;
+
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.UnionLayout;
+import java.util.List;
+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);
+ }
+
+ @Override
+ UnionLayoutImpl dup(long bitAlignment, Optional name) {
+ return new UnionLayoutImpl(memberLayouts(), bitAlignment, name);
+ }
+
+ public static UnionLayout of(List elements) {
+ return new UnionLayoutImpl(elements);
+ }
+
+}
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
new file mode 100644
index 0000000000000..079cb123a39a9
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2019, 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 jdk.internal.foreign.layout;
+
+import jdk.internal.foreign.Utils;
+import jdk.internal.misc.Unsafe;
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.Reflection;
+import jdk.internal.vm.annotation.ForceInline;
+import jdk.internal.vm.annotation.Stable;
+import sun.invoke.util.Wrapper;
+
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A value layout. A value layout is used to model the memory layout associated with values of basic data types, such as integral types
+ * (either signed or unsigned) and floating-point types. Each value layout has a size, an alignment (in bits),
+ * a {@linkplain ByteOrder byte order}, and a carrier, that is, the Java type that should be used when
+ * {@linkplain MemorySegment#get(ValueLayout.OfInt, long) accessing} a memory region using the value layout.
+ *
+ * This class defines useful value layout constants for Java primitive types and addresses.
+ * The layout constants in this class make implicit alignment and byte-ordering assumption: all layout
+ * constants in this class are byte-aligned, and their byte order is set to the {@linkplain ByteOrder#nativeOrder() platform default},
+ * thus making it easy to work with other APIs, such as arrays and {@link java.nio.ByteBuffer}.
+ *
+ * @implSpec This class and its subclasses are immutable, thread-safe and value-based.
+ */
+public final class ValueLayouts {
+
+ private ValueLayouts() {
+ }
+
+ abstract sealed static class AbstractValueLayout & ValueLayout> extends AbstractLayout {
+
+ static final int ADDRESS_SIZE_BITS = Unsafe.ADDRESS_SIZE * 8;
+
+ private final Class> carrier;
+ private final ByteOrder order;
+ @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);
+ }
+
+ /**
+ * {@return the value's byte order}
+ */
+ public final ByteOrder order() {
+ return order;
+ }
+
+ /**
+ * Returns a value layout with the same carrier, alignment constraints and name as this value layout,
+ * but with the specified byte order.
+ *
+ * @param order the desired byte order.
+ * @return a value layout with the given byte order.
+ */
+ abstract V withOrder(ByteOrder order);
+
+ @Override
+ public final String toString() {
+ char descriptor = carrier == MemorySegment.class ? 'A' : carrier.descriptorString().charAt(0);
+ if (order == ByteOrder.LITTLE_ENDIAN) {
+ descriptor = Character.toLowerCase(descriptor);
+ }
+ return decorateLayoutString(String.format("%s%d", descriptor, bitSize()));
+ }
+
+ @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);
+ }
+
+ public final VarHandle arrayElementVarHandle(int... shape) {
+ Objects.requireNonNull(shape);
+ MemoryLayout layout = self();
+ List path = new ArrayList<>();
+ for (int i = shape.length; i > 0; i--) {
+ int size = shape[i - 1];
+ if (size < 0) throw new IllegalArgumentException("Invalid shape size: " + size);
+ layout = MemoryLayout.sequenceLayout(size, layout);
+ path.add(MemoryLayout.PathElement.sequenceElement());
+ }
+ layout = MemoryLayout.sequenceLayout(layout);
+ path.add(MemoryLayout.PathElement.sequenceElement());
+ return layout.varHandle(path.toArray(new MemoryLayout.PathElement[0]));
+ }
+
+ /**
+ * {@return the carrier associated with this value layout}
+ */
+ public final Class> carrier() {
+ return carrier;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), order, carrier);
+ }
+
+ @Override
+ abstract V dup(long bitAlignment, Optional 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);
+ }
+ }
+ }
+
+ static boolean isValidCarrier(Class> carrier) {
+ return carrier == boolean.class
+ || carrier == byte.class
+ || carrier == short.class
+ || carrier == char.class
+ || carrier == int.class
+ || carrier == long.class
+ || carrier == float.class
+ || carrier == double.class
+ || carrier == MemorySegment.class;
+ }
+
+
+ @ForceInline
+ public final VarHandle accessHandle() {
+ if (handle == null) {
+ // this store to stable field is safe, because return value of 'makeMemoryAccessVarHandle' has stable identity
+ handle = Utils.makeSegmentViewVarHandle(self());
+ }
+ return handle;
+ }
+
+ @SuppressWarnings("unchecked")
+ final V self() {
+ return (V) this;
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfBoolean of(ByteOrder order) {
+ return new OfBooleanImpl(order);
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfByte of(ByteOrder order) {
+ return new OfByteImpl(order);
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfChar of(ByteOrder order) {
+ return new OfCharImpl(order);
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfShort of(ByteOrder order) {
+ return new OfShortImpl(order);
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfInt of(ByteOrder order) {
+ return new OfIntImpl(order);
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfFloat of(ByteOrder order) {
+ return new OfFloatImpl(order);
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfLong of(ByteOrder order) {
+ return new OfLongImpl(order);
+ }
+ }
+
+ 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);
+ }
+
+ @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());
+ }
+
+ public static OfDouble of(ByteOrder order) {
+ return new OfDoubleImpl(order);
+ }
+
+ }
+
+ public static final class OfAddressImpl extends AbstractValueLayout implements ValueLayout.OfAddress {
+
+ private final boolean isUnbounded;
+
+ 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;
+ }
+
+ @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());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ ((OfAddressImpl) other).isUnbounded == this.isUnbounded;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), isUnbounded);
+ }
+
+ @Override
+ @CallerSensitive
+ public OfAddress asUnbounded() {
+ Reflection.ensureNativeAccess(Reflection.getCallerClass(), OfAddress.class, "asUnbounded");
+ return new OfAddressImpl(order(), bitSize(), bitAlignment(), true, name());
+ }
+
+ @Override
+ public boolean isUnbounded() {
+ return isUnbounded;
+ }
+
+ public static OfAddress of(ByteOrder order) {
+ return new OfAddressImpl(order);
+ }
+ }
+
+}
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 c0d9f4e60b350..181f0e749b858 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 enum Feature {
RECORD_PATTERNS,
@JEP(number=425, title="Virtual Threads")
VIRTUAL_THREADS,
- @JEP(number=424, title="Foreign Function & Memory API")
+ @JEP(number=434, title="Foreign Function & Memory API", status="Second Preview")
FOREIGN,
/**
* A key for testing.
diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
index ddb06be377530..80faf5ecf9a1f 100644
--- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
@@ -789,7 +789,7 @@ public static boolean hasEnableNativeAccessFlag() {
private static void addEnableNativeAccess(ModuleLayer layer) {
for (String name : NATIVE_ACCESS_MODULES) {
if (name.equals("ALL-UNNAMED")) {
- JLA.addEnableNativeAccessAllUnnamed();
+ JLA.addEnableNativeAccessToAllUnnamed();
} else {
Optional module = layer.findModule(name);
if (module.isPresent()) {
diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java
index 7171b4fb53d2e..81eaf1545ffdd 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java
@@ -115,33 +115,7 @@ public static void ensureNativeAccess(Class> currentClass, Class> owner, Str
Module module = currentClass != null ?
currentClass.getModule() :
ClassLoader.getSystemClassLoader().getUnnamedModule();
- boolean isNativeAccessEnabled = SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module);
- if (!isNativeAccessEnabled) {
- synchronized(module) {
- isNativeAccessEnabled = SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module);
- if (isNativeAccessEnabled) {
- // some other thread got to it, do nothing
- } else if (ModuleBootstrap.hasEnableNativeAccessFlag()) {
- throw new IllegalCallerException("Illegal native access from: " + module);
- } else {
- // warn and set flag, so that only one warning is reported per module
- String cls = owner.getName();
- String mtd = cls + "::" + methodName;
- String mod = module.isNamed() ? "module " + module.getName() : "the unnamed module";
- String modflag = module.isNamed() ? module.getName() : "ALL-UNNAMED";
- System.err.printf("""
- WARNING: A restricted method in %s has been called
- WARNING: %s has been called by %s
- WARNING: Use --enable-native-access=%s to avoid a warning for this module
- %n""", cls, mtd, mod, modflag);
- if (module.isNamed()) {
- SharedSecrets.getJavaLangAccess().addEnableNativeAccess(module);
- } else {
- SharedSecrets.getJavaLangAccess().addEnableNativeAccessAllUnnamed();
- }
- }
- }
- }
+ SharedSecrets.getJavaLangAccess().ensureNativeAccess(module, owner, methodName);
}
/**
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 b57b56bb051e5..d203910bf8ca8 100644
--- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
@@ -29,7 +29,7 @@
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.lang.ref.Cleaner.Cleanable;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
@@ -1208,12 +1208,12 @@ public MappedByteBuffer map(MapMode mode, long position, long size) throws IOExc
@Override
public MemorySegment map(MapMode mode, long offset, long size,
- MemorySession session)
+ SegmentScope session)
throws IOException
{
Objects.requireNonNull(mode,"Mode is null");
Objects.requireNonNull(session, "Session is null");
- MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session);
+ MemorySessionImpl sessionImpl = (MemorySessionImpl) session;
sessionImpl.checkValidState();
if (offset < 0)
throw new IllegalArgumentException("Requested bytes offset must be >= 0.");
diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java
index aed3c9350de42..25f1813412726 100644
--- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java
+++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java
@@ -50,7 +50,7 @@ public class TestLinkToNativeRBP {
final static Linker abi = Linker.nativeLinker();
static final SymbolLookup lookup = SymbolLookup.loaderLookup();
- final static MethodHandle foo = abi.downcallHandle(lookup.lookup("foo").get(),
+ final static MethodHandle foo = abi.downcallHandle(lookup.find("foo").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT));
static int foo() throws Throwable {
diff --git a/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java b/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java
index 0192118235302..0c41fe75791f1 100644
--- a/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java
+++ b/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java
@@ -42,7 +42,7 @@ public class ClassFileVersionTest {
* compilation. If a particular class becomes non-preview, any
* currently preview class can be substituted in.
*/
- private static final Class> PREVIEW_API = java.lang.foreign.MemoryAddress.class;
+ private static final Class> PREVIEW_API = java.lang.foreign.MemorySegment.class;
static Method m;
public static void testIt(String className, int expectedResult) throws Exception {
diff --git a/test/jdk/com/sun/jdi/JdbLastErrorTest.java b/test/jdk/com/sun/jdi/JdbLastErrorTest.java
index c5eceb855e191..9cde54207a37f 100644
--- a/test/jdk/com/sun/jdi/JdbLastErrorTest.java
+++ b/test/jdk/com/sun/jdi/JdbLastErrorTest.java
@@ -54,10 +54,10 @@ private static void testWindows() throws Throwable {
System.loadLibrary("Kernel32");
SymbolLookup lookup = SymbolLookup.loaderLookup();
MethodHandle getLastError = linker.downcallHandle(
- lookup.lookup("GetLastError").orElseThrow(),
+ lookup.find("GetLastError").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_INT));
MethodHandle setLastError = linker.downcallHandle(
- lookup.lookup("SetLastError").orElseThrow(),
+ lookup.find("SetLastError").orElseThrow(),
FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT));
for (int i = 0; i < 10; i++) {
diff --git a/test/jdk/java/foreign/CallGeneratorHelper.java b/test/jdk/java/foreign/CallGeneratorHelper.java
index f075616f5827d..e1ef460603bc7 100644
--- a/test/jdk/java/foreign/CallGeneratorHelper.java
+++ b/test/jdk/java/foreign/CallGeneratorHelper.java
@@ -22,17 +22,9 @@
*
*/
-import java.lang.foreign.Addressable;
-import java.lang.foreign.Linker;
-import java.lang.foreign.FunctionDescriptor;
-import java.lang.foreign.GroupLayout;
-import java.lang.foreign.MemoryAddress;
-import java.lang.foreign.MemoryLayout;
-import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
-import java.lang.foreign.SegmentAllocator;
-import java.lang.foreign.ValueLayout;
+import java.lang.foreign.*;
+import java.lang.foreign.SegmentScope;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
@@ -387,21 +379,21 @@ static void generateUpcallFunction(String fName, Ret ret, List params
@SuppressWarnings("unchecked")
static Object makeArg(MemoryLayout layout, List> checks, boolean check) throws ReflectiveOperationException {
if (layout instanceof GroupLayout) {
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
initStruct(segment, (GroupLayout)layout, checks, check);
return segment;
} else if (isPointer(layout)) {
- MemorySegment segment = MemorySegment.allocateNative(1, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(1L, SegmentScope.auto());
if (check) {
checks.add(o -> {
try {
- assertEquals(o, segment.address());
+ assertEquals(o, segment);
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
});
}
- return segment.address();
+ return segment;
} else if (layout instanceof ValueLayout) {
if (isIntegral(layout)) {
if (check) {
@@ -426,7 +418,7 @@ static Object makeArg(MemoryLayout layout, List> checks, boolea
static void initStruct(MemorySegment str, GroupLayout g, List> checks, boolean check) throws ReflectiveOperationException {
for (MemoryLayout l : g.memberLayouts()) {
- if (l.isPadding()) continue;
+ if (l instanceof PaddingLayout) continue;
VarHandle accessor = g.varHandle(MemoryLayout.PathElement.groupElement(l.name().get()));
List> fieldsCheck = new ArrayList<>();
Object value = makeArg(l, fieldsCheck, check);
@@ -447,19 +439,17 @@ static void initStruct(MemorySegment str, GroupLayout g, List>
}
}
- static Class> carrier(MemoryLayout layout, boolean param) {
+ static Class> carrier(MemoryLayout layout) {
if (layout instanceof GroupLayout) {
return MemorySegment.class;
- } if (isPointer(layout)) {
- return param ? Addressable.class : MemoryAddress.class;
- } else if (layout instanceof ValueLayout valueLayout) {
+ } if (layout instanceof ValueLayout valueLayout) {
return valueLayout.carrier();
} else {
throw new IllegalStateException("Unexpected layout: " + layout);
}
}
- MethodHandle downcallHandle(Linker abi, Addressable symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) {
+ MethodHandle downcallHandle(Linker abi, MemorySegment symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) {
MethodHandle mh = abi.downcallHandle(symbol, descriptor);
if (descriptor.returnLayout().isPresent() && descriptor.returnLayout().get() instanceof GroupLayout) {
mh = mh.bindTo(allocator);
diff --git a/test/jdk/java/foreign/LibraryLookupTest.java b/test/jdk/java/foreign/LibraryLookupTest.java
index 9a7f321807ef9..b5108b5a63efb 100644
--- a/test/jdk/java/foreign/LibraryLookupTest.java
+++ b/test/jdk/java/foreign/LibraryLookupTest.java
@@ -23,11 +23,11 @@
import org.testng.annotations.Test;
-import java.lang.foreign.Addressable;
+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.MemorySession;
import java.lang.foreign.SymbolLookup;
import java.lang.invoke.MethodHandle;
import java.nio.file.Path;
@@ -51,12 +51,12 @@ public class LibraryLookupTest {
@Test
void testLoadLibraryConfined() {
- try (MemorySession session0 = MemorySession.openConfined()) {
- callFunc(loadLibrary(session0));
- try (MemorySession session1 = MemorySession.openConfined()) {
- callFunc(loadLibrary(session1));
- try (MemorySession session2 = MemorySession.openConfined()) {
- callFunc(loadLibrary(session2));
+ 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()));
}
}
}
@@ -64,21 +64,21 @@ void testLoadLibraryConfined() {
@Test(expectedExceptions = IllegalStateException.class)
void testLoadLibraryConfinedClosed() {
- Addressable addr;
- try (MemorySession session = MemorySession.openConfined()) {
- addr = loadLibrary(session);
+ MemorySegment addr;
+ try (Arena arena = Arena.openConfined()) {
+ addr = loadLibrary(arena.scope());
}
callFunc(addr);
}
- private static Addressable loadLibrary(MemorySession session) {
+ private static MemorySegment loadLibrary(SegmentScope session) {
SymbolLookup lib = SymbolLookup.libraryLookup(LIB_PATH, session);
- MemorySegment addr = lib.lookup("inc").get();
- assertEquals(addr.session(), session);
+ MemorySegment addr = lib.find("inc").get();
+ assertEquals(addr.scope(), session);
return addr;
}
- private static void callFunc(Addressable addr) {
+ private static void callFunc(MemorySegment addr) {
try {
INC.invokeExact(addr);
} catch (IllegalStateException ex) {
@@ -94,12 +94,12 @@ private static void callFunc(Addressable addr) {
@Test(expectedExceptions = IllegalArgumentException.class)
void testBadLibraryLookupName() {
- SymbolLookup.libraryLookup("nonExistent", MemorySession.global());
+ SymbolLookup.libraryLookup("nonExistent", SegmentScope.global());
}
@Test(expectedExceptions = IllegalArgumentException.class)
void testBadLibraryLookupPath() {
- SymbolLookup.libraryLookup(Path.of("nonExistent"), MemorySession.global());
+ SymbolLookup.libraryLookup(Path.of("nonExistent"), SegmentScope.global());
}
@Test
@@ -116,8 +116,8 @@ static class LibraryLoadAndAccess implements Runnable {
@Override
public void run() {
for (int i = 0 ; i < ITERATIONS ; i++) {
- try (MemorySession session = MemorySession.openConfined()) {
- callFunc(loadLibrary(session));
+ try (Arena arena = Arena.openConfined()) {
+ callFunc(loadLibrary(arena.scope()));
}
}
}
@@ -125,15 +125,15 @@ public void run() {
@Test
void testLoadLibrarySharedClosed() throws Throwable {
- MemorySession session = MemorySession.openShared();
- Addressable addr = loadLibrary(session);
+ Arena arena = Arena.openShared();
+ MemorySegment addr = loadLibrary(arena.scope());
ExecutorService accessExecutor = Executors.newCachedThreadPool();
for (int i = 0; i < NUM_ACCESSORS ; i++) {
accessExecutor.execute(new LibraryAccess(addr));
}
while (true) {
try {
- session.close();
+ arena.close();
break;
} catch (IllegalStateException ex) {
// wait for addressable parameter to be released
@@ -146,9 +146,9 @@ void testLoadLibrarySharedClosed() throws Throwable {
static class LibraryAccess implements Runnable {
- final Addressable addr;
+ final MemorySegment addr;
- LibraryAccess(Addressable addr) {
+ LibraryAccess(MemorySegment addr) {
this.addr = addr;
}
diff --git a/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java b/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java
new file mode 100644
index 0000000000000..49d4f7d4f697f
--- /dev/null
+++ b/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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
+ * @run testng/othervm MemoryLayoutPrincipalTotalityTest
+ */
+
+import org.testng.annotations.*;
+
+import java.lang.foreign.*;
+
+import static java.lang.foreign.ValueLayout.*;
+import static org.testng.Assert.*;
+
+public class MemoryLayoutPrincipalTotalityTest extends NativeTestHelper {
+
+ // The tests in this class is mostly there to ensure compile-time pattern matching totality.
+
+ @Test
+ public void testBasicTotality() {
+ MemoryLayout memoryLayout = javaIntMemoryLayout();
+ int v0 = switch (memoryLayout) {
+ case MemoryLayout ml -> 1;
+ };
+ assertEquals(v0, 1);
+ }
+
+ @Test
+ public void testMLRemovedTotality() {
+ MemoryLayout memoryLayout = javaIntMemoryLayout();
+ var v1 = switch (memoryLayout) {
+ case GroupLayout gl -> 0;
+ case PaddingLayout pl -> 0; // leaf
+ case SequenceLayout sl -> 0; // leaf
+ case ValueLayout vl -> 1;
+ };
+ assertEquals(v1, 1);
+ }
+
+ @Test
+ public void testMLGLRemovedTotality() {
+ MemoryLayout memoryLayout = javaIntMemoryLayout();
+ var v2 = switch (memoryLayout) {
+ case PaddingLayout pl -> 0; // leaf
+ case SequenceLayout sl -> 0; // leaf
+ case ValueLayout vl -> 1;
+ case StructLayout sl -> 0; // leaf
+ case UnionLayout ul -> 0; // leaf
+ };
+ assertEquals(v2, 1);
+ }
+
+ @Test
+ public void testMLGLVLRemovedTotality() {
+ MemoryLayout memoryLayout = javaIntMemoryLayout();
+ var v3 = switch (memoryLayout) {
+ case PaddingLayout pl -> 0; // leaf
+ case SequenceLayout sl -> 0; // leaf
+ case StructLayout sl -> 0; // leaf
+ case UnionLayout ul -> 0; // leaf
+ case OfAddress oa -> 0; // leaf
+ case OfBoolean ob -> 0; // leaf
+ case OfByte ob -> 0; // leaf
+ case OfChar oc -> 0; // leaf
+ case OfDouble od -> 0; // leaf
+ case OfFloat of -> 0; // leaf
+ case OfInt oi -> 1; // leaf
+ case OfLong ol -> 0; // leaf
+ case OfShort os -> 0; // leaf
+ };
+ assertEquals(v3, 1);
+ }
+
+ @Test
+ public void testMLVLRemovedTotality() {
+ MemoryLayout memoryLayout = javaIntMemoryLayout();
+ var v4 = switch (memoryLayout) {
+ case GroupLayout gl -> 0;
+ case PaddingLayout pl -> 0; // leaf
+ case SequenceLayout sl -> 0; // leaf
+ case OfAddress oa -> 0; // leaf
+ case OfBoolean ob -> 0; // leaf
+ case OfByte ob -> 0; // leaf
+ case OfChar oc -> 0; // leaf
+ case OfDouble od -> 0; // leaf
+ case OfFloat of -> 0; // leaf
+ case OfInt oi -> 1; // leaf
+ case OfLong ol -> 0; // leaf
+ case OfShort os -> 0; // leaf
+ };
+ assertEquals(v4, 1);
+ }
+
+ private static MemoryLayout javaIntMemoryLayout() {
+ return JAVA_INT;
+ }
+
+}
diff --git a/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java b/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java
new file mode 100644
index 0000000000000..9abccdaf9f8e6
--- /dev/null
+++ b/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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
+ * @run testng/othervm MemoryLayoutTypeRetentionTest
+ */
+
+import org.testng.annotations.*;
+
+import java.lang.foreign.*;
+import java.nio.ByteOrder;
+
+import static java.lang.foreign.ValueLayout.*;
+import static org.testng.Assert.*;
+
+public class MemoryLayoutTypeRetentionTest extends NativeTestHelper {
+
+ // These tests check both compile-time and runtime properties.
+ // 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;
+
+ @Test
+ public void testOfBoolean() {
+ OfBoolean v = JAVA_BOOLEAN
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfByte() {
+ OfByte v = JAVA_BYTE
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfShort() {
+ OfShort v = JAVA_SHORT
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfInt() {
+ OfInt v = JAVA_INT
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfChar() {
+ OfChar v = JAVA_CHAR
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfLong() {
+ OfLong v = JAVA_LONG
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfFloat() {
+ OfFloat v = JAVA_FLOAT
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfDouble() {
+ OfDouble v = JAVA_DOUBLE
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ }
+
+ @Test
+ public void testOfAddress() {
+ OfAddress v = ADDRESS
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME)
+ .withOrder(BYTE_ORDER);
+ check(v);
+ assertFalse(v.isUnbounded());
+ OfAddress v2 = v.asUnbounded();
+ assertTrue(v2.isUnbounded());
+ }
+
+ @Test
+ public void testPaddingLayout() {
+ PaddingLayout v = MemoryLayout.paddingLayout(8)
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME);
+ check(v);
+ }
+
+ @Test
+ public void testGroupLayout() {
+ GroupLayout v = MemoryLayout.structLayout(JAVA_INT, JAVA_LONG)
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME);
+ check(v);
+ }
+
+ @Test
+ public void testStructLayout() {
+ StructLayout v = MemoryLayout.structLayout(JAVA_INT, JAVA_LONG)
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME);
+ check(v);
+ }
+
+ @Test
+ public void testUnionLayout() {
+ UnionLayout v = MemoryLayout.unionLayout(JAVA_INT, JAVA_LONG)
+ .withBitAlignment(BIT_ALIGNMENT)
+ .withName(NAME);
+ check(v);
+ }
+
+ public void check(ValueLayout v) {
+ check((MemoryLayout) v);
+ assertEquals(v.order(), BYTE_ORDER);
+ }
+
+ public void check(MemoryLayout v) {
+ assertEquals(v.name().orElseThrow(), NAME);
+ 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 e4fa9cc6be37e..2d48f2e386cb6 100644
--- a/test/jdk/java/foreign/NativeTestHelper.java
+++ b/test/jdk/java/foreign/NativeTestHelper.java
@@ -22,18 +22,21 @@
*
*/
-import java.lang.foreign.Addressable;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
-import java.lang.foreign.MemoryAddress;
import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.SegmentScope;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
public class NativeTestHelper {
+ public static final boolean IS_WINDOWS = System.getProperty("os.name").startsWith("Windows");
+
public static boolean isIntegral(MemoryLayout layout) {
return layout instanceof ValueLayout valueLayout && isIntegral(valueLayout.carrier());
}
@@ -44,7 +47,7 @@ static boolean isIntegral(Class> clazz) {
}
public static boolean isPointer(MemoryLayout layout) {
- return layout instanceof ValueLayout valueLayout && valueLayout.carrier() == MemoryAddress.class;
+ return layout instanceof ValueLayout valueLayout && valueLayout.carrier() == MemorySegment.class;
}
// the constants below are useful aliases for C types. The type/carrier association is only valid for 64-bit platforms.
@@ -81,17 +84,17 @@ public static boolean isPointer(MemoryLayout layout) {
/**
* The {@code T*} native type.
*/
- public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64);
+ public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded();
- private static Linker LINKER = Linker.nativeLinker();
+ private static final Linker LINKER = Linker.nativeLinker();
private static final MethodHandle FREE = LINKER.downcallHandle(
- LINKER.defaultLookup().lookup("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
+ LINKER.defaultLookup().find("free").get(), FunctionDescriptor.ofVoid(C_POINTER));
private static final MethodHandle MALLOC = LINKER.downcallHandle(
- LINKER.defaultLookup().lookup("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG));
+ LINKER.defaultLookup().find("malloc").get(), FunctionDescriptor.of(C_POINTER, C_LONG_LONG));
- public static void freeMemory(Addressable address) {
+ public static void freeMemory(MemorySegment address) {
try {
FREE.invokeExact(address);
} catch (Throwable ex) {
@@ -99,15 +102,28 @@ public static void freeMemory(Addressable address) {
}
}
- public static MemoryAddress allocateMemory(long size) {
+ public static MemorySegment allocateMemory(long size) {
try {
- return (MemoryAddress)MALLOC.invokeExact(size);
+ return (MemorySegment) MALLOC.invokeExact(size);
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}
- public static Addressable findNativeOrThrow(String name) {
- return SymbolLookup.loaderLookup().lookup(name).orElseThrow();
+ public static MemorySegment findNativeOrThrow(String name) {
+ return SymbolLookup.loaderLookup().find(name).orElseThrow();
+ }
+
+ public static MethodHandle downcallHandle(String symbol, FunctionDescriptor desc, Linker.Option... options) {
+ return LINKER.downcallHandle(findNativeOrThrow(symbol), desc, options);
+ }
+
+ 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());
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
}
}
diff --git a/test/jdk/java/foreign/SafeFunctionAccessTest.java b/test/jdk/java/foreign/SafeFunctionAccessTest.java
index f638814c4f9fb..5873afb2308d2 100644
--- a/test/jdk/java/foreign/SafeFunctionAccessTest.java
+++ b/test/jdk/java/foreign/SafeFunctionAccessTest.java
@@ -28,18 +28,20 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED SafeFunctionAccessTest
*/
-import java.lang.foreign.Addressable;
+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.MemorySession;
+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.*;
import static org.testng.Assert.*;
@@ -56,10 +58,10 @@ public class SafeFunctionAccessTest extends NativeTestHelper {
@Test(expectedExceptions = IllegalStateException.class)
public void testClosedStruct() throws Throwable {
MemorySegment segment;
- try (MemorySession session = MemorySession.openConfined()) {
- segment = MemorySegment.allocateNative(POINT, session);
+ try (Arena arena = Arena.openConfined()) {
+ segment = arena.allocate(POINT);
}
- assertFalse(segment.session().isAlive());
+ assertFalse(segment.scope().isAlive());
MethodHandle handle = Linker.nativeLinker().downcallHandle(
findNativeOrThrow("struct_func"),
FunctionDescriptor.ofVoid(POINT));
@@ -72,33 +74,39 @@ public void testClosedStructAddr_6() throws Throwable {
MethodHandle handle = Linker.nativeLinker().downcallHandle(
findNativeOrThrow("addr_func_6"),
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();
+ return new Allocation(arena, arena.allocate(layout));
+ }
+ }
for (int i = 0 ; i < 6 ; i++) {
- MemorySegment[] segments = new MemorySegment[]{
- MemorySegment.allocateNative(POINT, MemorySession.openShared()),
- MemorySegment.allocateNative(POINT, MemorySession.openShared()),
- MemorySegment.allocateNative(POINT, MemorySession.openShared()),
- MemorySegment.allocateNative(POINT, MemorySession.openShared()),
- MemorySegment.allocateNative(POINT, MemorySession.openShared()),
- MemorySegment.allocateNative(POINT, MemorySession.openShared())
+ Allocation[] allocations = new Allocation[]{
+ Allocation.of(POINT),
+ Allocation.of(POINT),
+ Allocation.of(POINT),
+ Allocation.of(POINT),
+ Allocation.of(POINT),
+ Allocation.of(POINT)
};
// check liveness
- segments[i].session().close();
+ allocations[i].drop().close();
for (int j = 0 ; j < 6 ; j++) {
if (i == j) {
- assertFalse(segments[j].session().isAlive());
+ assertFalse(allocations[j].drop().scope().isAlive());
} else {
- assertTrue(segments[j].session().isAlive());
+ assertTrue(allocations[j].drop().scope().isAlive());
}
}
try {
- handle.invokeWithArguments(segments);
+ handle.invokeWithArguments(Stream.of(allocations).map(Allocation::segment).toArray());
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("Already closed"));
}
for (int j = 0 ; j < 6 ; j++) {
if (i != j) {
- segments[j].session().close(); // should succeed!
+ allocations[j].drop().close(); // should succeed!
}
}
}
@@ -107,30 +115,30 @@ public void testClosedStructAddr_6() throws Throwable {
@Test(expectedExceptions = IllegalStateException.class)
public void testClosedVaList() throws Throwable {
VaList list;
- try (MemorySession session = MemorySession.openConfined()) {
- list = VaList.make(b -> b.addVarg(C_INT, 42), session);
+ try (Arena arena = Arena.openConfined()) {
+ list = VaList.make(b -> b.addVarg(C_INT, 42), arena.scope());
}
- assertFalse(list.session().isAlive());
+ assertFalse(list.segment().scope().isAlive());
MethodHandle handle = Linker.nativeLinker().downcallHandle(
findNativeOrThrow("addr_func"),
FunctionDescriptor.ofVoid(C_POINTER));
- handle.invokeExact((Addressable)list);
+ handle.invokeExact(list.segment());
}
@Test(expectedExceptions = IllegalStateException.class)
public void testClosedUpcall() throws Throwable {
MemorySegment upcall;
- try (MemorySession session = MemorySession.openConfined()) {
+ try (Arena arena = Arena.openConfined()) {
MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class));
- upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), session);
+ upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena.scope());
}
- assertFalse(upcall.session().isAlive());
+ assertFalse(upcall.scope().isAlive());
MethodHandle handle = Linker.nativeLinker().downcallHandle(
findNativeOrThrow("addr_func"),
FunctionDescriptor.ofVoid(C_POINTER));
- handle.invokeExact((Addressable)upcall);
+ handle.invokeExact(upcall);
}
static void dummy() { }
@@ -141,9 +149,9 @@ public void testClosedVaListCallback() throws Throwable {
findNativeOrThrow("addr_func_cb"),
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
- try (MemorySession session = MemorySession.openConfined()) {
- VaList list = VaList.make(b -> b.addVarg(C_INT, 42), session);
- handle.invoke(list, sessionChecker(session));
+ try (Arena arena = Arena.openConfined()) {
+ VaList list = VaList.make(b -> b.addVarg(C_INT, 42), arena.scope());
+ handle.invokeExact(list.segment(), sessionChecker(arena));
}
}
@@ -153,9 +161,9 @@ public void testClosedStructCallback() throws Throwable {
findNativeOrThrow("addr_func_cb"),
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(POINT, session);
- handle.invoke(segment, sessionChecker(session));
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = arena.allocate(POINT);
+ handle.invokeExact(segment, sessionChecker(arena));
}
}
@@ -165,27 +173,27 @@ public void testClosedUpcallCallback() throws Throwable {
findNativeOrThrow("addr_func_cb"),
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
- try (MemorySession session = MemorySession.openConfined()) {
+ try (Arena arena = Arena.openConfined()) {
MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class));
- MemorySegment upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), session);
- handle.invoke(upcall, sessionChecker(session));
+ MemorySegment upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena.scope());
+ handle.invokeExact(upcall, sessionChecker(arena));
}
}
- MemorySegment sessionChecker(MemorySession session) {
+ MemorySegment sessionChecker(Arena arena) {
try {
MethodHandle handle = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "checkSession",
- MethodType.methodType(void.class, MemorySession.class));
- handle = handle.bindTo(session);
- return Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), MemorySession.openImplicit());
+ MethodType.methodType(void.class, Arena.class));
+ handle = handle.bindTo(arena);
+ return Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), SegmentScope.auto());
} catch (Throwable ex) {
throw new AssertionError(ex);
}
}
- static void checkSession(MemorySession session) {
+ static void checkSession(Arena arena) {
try {
- session.close();
+ arena.close();
fail("Session closed unexpectedly!");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("acquired")); //if acquired, fine
diff --git a/test/jdk/java/foreign/StdLibTest.java b/test/jdk/java/foreign/StdLibTest.java
index 98bf9f50703cd..05c4faac992ab 100644
--- a/test/jdk/java/foreign/StdLibTest.java
+++ b/test/jdk/java/foreign/StdLibTest.java
@@ -152,37 +152,35 @@ void test_vprintf(List args) throws Throwable {
static class StdLibHelper {
- final static MethodHandle strcat = abi.downcallHandle(abi.defaultLookup().lookup("strcat").get(),
- FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER))
- .asType(MethodType.methodType(MemoryAddress.class, MemorySegment.class, MemorySegment.class)); // exact signature match
+ final static MethodHandle strcat = abi.downcallHandle(abi.defaultLookup().find("strcat").get(),
+ FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER));
-
- final static MethodHandle strcmp = abi.downcallHandle(abi.defaultLookup().lookup("strcmp").get(),
+ final static MethodHandle strcmp = abi.downcallHandle(abi.defaultLookup().find("strcmp").get(),
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
- final static MethodHandle puts = abi.downcallHandle(abi.defaultLookup().lookup("puts").get(),
+ final static MethodHandle puts = abi.downcallHandle(abi.defaultLookup().find("puts").get(),
FunctionDescriptor.of(C_INT, C_POINTER));
- final static MethodHandle strlen = abi.downcallHandle(abi.defaultLookup().lookup("strlen").get(),
+ final static MethodHandle strlen = abi.downcallHandle(abi.defaultLookup().find("strlen").get(),
FunctionDescriptor.of(C_INT, C_POINTER));
- final static MethodHandle gmtime = abi.downcallHandle(abi.defaultLookup().lookup("gmtime").get(),
+ final static MethodHandle gmtime = abi.downcallHandle(abi.defaultLookup().find("gmtime").get(),
FunctionDescriptor.of(C_POINTER, C_POINTER));
- final static MethodHandle qsort = abi.downcallHandle(abi.defaultLookup().lookup("qsort").get(),
+ 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 MethodHandle qsortCompar;
- final static MethodHandle rand = abi.downcallHandle(abi.defaultLookup().lookup("rand").get(),
+ final static MethodHandle rand = abi.downcallHandle(abi.defaultLookup().find("rand").get(),
FunctionDescriptor.of(C_INT));
- final static MethodHandle vprintf = abi.downcallHandle(abi.defaultLookup().lookup("vprintf").get(),
+ final static MethodHandle vprintf = abi.downcallHandle(abi.defaultLookup().find("vprintf").get(),
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
- final static Addressable printfAddr = abi.defaultLookup().lookup("printf").get();
+ final static MemorySegment printfAddr = abi.defaultLookup().find("printf").get();
final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
@@ -190,48 +188,48 @@ static class StdLibHelper {
try {
//qsort upcall handle
qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
- Linker.upcallType(qsortComparFunction));
+ qsortComparFunction.toMethodType());
} catch (ReflectiveOperationException ex) {
throw new IllegalStateException(ex);
}
}
String strcat(String s1, String s2) throws Throwable {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment buf = session.allocate(s1.length() + s2.length() + 1);
+ try (var arena = Arena.openConfined()) {
+ MemorySegment buf = arena.allocate(s1.length() + s2.length() + 1);
buf.setUtf8String(0, s1);
- MemorySegment other = session.allocateUtf8String(s2);
- return ((MemoryAddress)strcat.invokeExact(buf, other)).getUtf8String(0);
+ MemorySegment other = arena.allocateUtf8String(s2);
+ return ((MemorySegment)strcat.invokeExact(buf, other)).getUtf8String(0);
}
}
int strcmp(String s1, String s2) throws Throwable {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment ns1 = session.allocateUtf8String(s1);
- MemorySegment ns2 = session.allocateUtf8String(s2);
- return (int)strcmp.invoke(ns1, ns2);
+ try (var arena = Arena.openConfined()) {
+ MemorySegment ns1 = arena.allocateUtf8String(s1);
+ MemorySegment ns2 = arena.allocateUtf8String(s2);
+ return (int)strcmp.invokeExact(ns1, ns2);
}
}
int puts(String msg) throws Throwable {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment s = session.allocateUtf8String(msg);
- return (int)puts.invoke(s);
+ try (var arena = Arena.openConfined()) {
+ MemorySegment s = arena.allocateUtf8String(msg);
+ return (int)puts.invokeExact(s);
}
}
int strlen(String msg) throws Throwable {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment s = session.allocateUtf8String(msg);
- return (int)strlen.invoke(s);
+ try (var arena = Arena.openConfined()) {
+ MemorySegment s = arena.allocateUtf8String(msg);
+ return (int)strlen.invokeExact(s);
}
}
Tm gmtime(long arg) throws Throwable {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment time = session.allocate(8);
+ try (var arena = Arena.openConfined()) {
+ MemorySegment time = arena.allocate(8);
time.set(C_LONG_LONG, 0, arg);
- return new Tm((MemoryAddress)gmtime.invoke(time));
+ return new Tm((MemorySegment)gmtime.invokeExact(time));
}
}
@@ -242,8 +240,8 @@ static class Tm {
static final long SIZE = 56;
- Tm(MemoryAddress addr) {
- this.base = MemorySegment.ofAddress(addr, SIZE, MemorySession.global());
+ Tm(MemorySegment addr) {
+ this.base = addr.asSlice(0, SIZE);
}
int sec() {
@@ -277,20 +275,20 @@ boolean isdst() {
int[] qsort(int[] arr) throws Throwable {
//init native array
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment nativeArr = session.allocateArray(C_INT, arr);
+ try (var arena = Arena.openConfined()) {
+ MemorySegment nativeArr = arena.allocateArray(C_INT, arr);
//call qsort
- Addressable qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, session);
+ MemorySegment qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, arena.scope());
- qsort.invoke(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub);
+ qsort.invokeExact(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub);
//convert back to Java array
return nativeArr.toArray(C_INT);
}
}
- static int qsortCompare(MemoryAddress addr1, MemoryAddress addr2) {
+ static int qsortCompare(MemorySegment addr1, MemorySegment addr2) {
return addr1.get(C_INT, 0) -
addr2.get(C_INT, 0);
}
@@ -300,32 +298,34 @@ int rand() throws Throwable {
}
int printf(String format, List args) throws Throwable {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment formatStr = session.allocateUtf8String(format);
- return (int)specializedPrintf(args).invoke(formatStr,
- args.stream().map(a -> a.nativeValue(session)).toArray());
+ try (var arena = Arena.openConfined()) {
+ 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 (MemorySession session = MemorySession.openConfined()) {
- MemorySegment formatStr = session.allocateUtf8String(format);
- VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, session)), session);
- return (int)vprintf.invoke(formatStr, vaList);
+ 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, MemoryAddress.class);
+ MethodType mt = MethodType.methodType(int.class, MemorySegment.class);
FunctionDescriptor fd = printfBase;
List variadicLayouts = new ArrayList<>(args.size());
for (PrintfArg arg : args) {
mt = mt.appendParameterTypes(arg.carrier);
variadicLayouts.add(arg.layout);
}
+ Linker.Option varargIndex = Linker.Option.firstVariadicArg(fd.argumentLayouts().size());
MethodHandle mh = abi.downcallHandle(printfAddr,
- fd.asVariadic(variadicLayouts.toArray(new MemoryLayout[args.size()])));
+ fd.appendArgumentLayouts(variadicLayouts.toArray(new MemoryLayout[args.size()])),
+ varargIndex);
return mh.asSpreader(1, Object[].class, args.size());
}
}
@@ -384,26 +384,24 @@ public static Object[][] printfArgs() {
.toArray(Object[][]::new);
}
- enum PrintfArg implements BiConsumer {
+ enum PrintfArg implements BiConsumer {
- INTEGRAL(int.class, C_INT, "%d", session -> 42, 42, VaList.Builder::addVarg),
- STRING(MemoryAddress.class, C_POINTER, "%s", session -> {
- var segment = MemorySegment.allocateNative(4, session);
- segment.setUtf8String(0, "str");
- return segment.address();
+ INTEGRAL(int.class, C_INT, "%d", arena -> 42, 42, VaList.Builder::addVarg),
+ STRING(MemorySegment.class, C_POINTER, "%s", arena -> {
+ return arena.allocateUtf8String("str");
}, "str", VaList.Builder::addVarg),
- CHAR(byte.class, C_CHAR, "%c", session -> (byte) 'h', 'h', (builder, layout, value) -> builder.addVarg(C_INT, (int)value)),
- DOUBLE(double.class, C_DOUBLE, "%.4f", session ->1.2345d, 1.2345d, 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);
final Class> carrier;
final ValueLayout layout;
final String format;
- final Function nativeValueFactory;
+ 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, VaListBuilderCall builderCall) {
this.carrier = carrier;
this.layout = layout;
this.format = format;
@@ -414,16 +412,16 @@ PrintfArg(Class> carrier, L layout, String format,
@Override
@SuppressWarnings("unchecked")
- public void accept(VaList.Builder builder, MemorySession session) {
- builderCall.build(builder, layout, nativeValueFactory.apply(session));
+ 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(MemorySession session) {
- return nativeValueFactory.apply(session);
+ public Object nativeValue(Arena arena) {
+ return nativeValueFactory.apply(arena);
}
}
diff --git a/test/jdk/java/foreign/TestAdaptVarHandles.java b/test/jdk/java/foreign/TestAdaptVarHandles.java
index c16701991c6e6..de74ed8c6b246 100644
--- a/test/jdk/java/foreign/TestAdaptVarHandles.java
+++ b/test/jdk/java/foreign/TestAdaptVarHandles.java
@@ -31,8 +31,9 @@
* @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.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.lang.foreign.ValueLayout;
import org.testng.annotations.*;
import static org.testng.Assert.*;
@@ -93,7 +94,7 @@ public class TestAdaptVarHandles {
@Test
public void testFilterValue() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
VarHandle intHandle = layout.varHandle();
VarHandle i2SHandle = MethodHandles.filterValue(intHandle, S2I, I2S);
i2SHandle.set(segment, "1");
@@ -112,7 +113,7 @@ public void testFilterValue() throws Throwable {
@Test
public void testFilterValueComposite() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
VarHandle intHandle = layout.varHandle();
MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class);
VarHandle i2SHandle = MethodHandles.filterValue(intHandle, CTX_S2I, CTX_I2S);
@@ -133,7 +134,7 @@ public void testFilterValueComposite() throws Throwable {
@Test
public void testFilterValueLoose() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
VarHandle intHandle = layout.varHandle();
VarHandle i2SHandle = MethodHandles.filterValue(intHandle, O2I, I2O);
i2SHandle.set(segment, "1");
@@ -190,8 +191,8 @@ public void testBadFilterUnboxException() {
public void testBadFilterBoxHandleException() {
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
VarHandle vh = MethodHandles.filterValue(intHandle, S2I, I2S_EX);
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, arena.scope());
vh.set(seg, "42");
String x = (String) vh.get(seg); // should throw
}
@@ -201,8 +202,8 @@ public void testBadFilterBoxHandleException() {
public void testBadFilterUnboxHandleException() {
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
VarHandle vh = MethodHandles.filterValue(intHandle, S2I_EX, I2S);
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, arena.scope());
vh.set(seg, "42"); // should throw
}
}
@@ -210,7 +211,7 @@ public void testBadFilterUnboxHandleException() {
@Test
public void testFilterCoordinates() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
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);
@@ -253,7 +254,7 @@ public void testBadFilterCoordinatesTooManyFilters() {
@Test
public void testInsertCoordinates() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
VarHandle intHandle_longIndex = MethodHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L);
intHandle_longIndex.set(1);
int oldValue = (int)intHandle_longIndex.getAndAdd(42);
@@ -291,7 +292,7 @@ public void testBadInsertCoordinatesTooManyValues() {
@Test
public void testPermuteCoordinates() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
VarHandle intHandle_swap = MethodHandles.permuteCoordinates(intHandleIndexed,
List.of(long.class, MemorySegment.class), 1, 0);
intHandle_swap.set(0L, segment, 1);
@@ -330,7 +331,7 @@ public void testBadPermuteCoordinatesIndexTooSmall() {
@Test
public void testCollectCoordinates() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
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);
@@ -373,7 +374,7 @@ public void testBadCollectCoordinatesWrongFilterException() {
@Test
public void testDropCoordinates() throws Throwable {
ValueLayout layout = ValueLayout.JAVA_INT;
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
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/TestArrays.java b/test/jdk/java/foreign/TestArrays.java
index 978993e1fb12d..e16dfb0d851c3 100644
--- a/test/jdk/java/foreign/TestArrays.java
+++ b/test/jdk/java/foreign/TestArrays.java
@@ -28,11 +28,11 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestArrays
*/
-import java.lang.foreign.MemoryAddress;
+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.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.lang.foreign.SequenceLayout;
import java.lang.invoke.VarHandle;
@@ -108,7 +108,7 @@ static void checkBytes(MemorySegment base, SequenceLayout layout, Function init, Consumer checker, MemoryLayout layout) {
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
init.accept(segment);
assertFalse(segment.isReadOnly());
checker.accept(segment);
@@ -119,7 +119,7 @@ public void testArrays(Consumer init, Consumer che
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(MemoryAddress.NULL, seq.byteSize(), MemorySession.global());
+ MemorySegment segment = MemorySegment.ofAddress(0, seq.byteSize(), SegmentScope.global());
arrayFactory.apply(segment);
}
@@ -127,8 +127,8 @@ public void testTooBigForArray(MemoryLayout layout, Function arrayFactory) {
if (layout.byteSize() == 1) throw new IllegalStateException(); //make it fail
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(layout.byteSize() + 1, layout.byteSize(), session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(layout.byteSize() + 1, layout.byteSize(), arena.scope());
arrayFactory.apply(segment);
}
}
@@ -136,8 +136,9 @@ public void testBadSize(MemoryLayout layout, Function arr
@Test(dataProvider = "elemLayouts",
expectedExceptions = IllegalStateException.class)
public void testArrayFromClosedSegment(MemoryLayout layout, Function arrayFactory) {
- MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openConfined());
- segment.session().close();
+ Arena arena = Arena.openConfined();
+ MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());
+ arena.close();
arrayFactory.apply(segment);
}
diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java
index 998a3197548bf..4f13b9112e627 100644
--- a/test/jdk/java/foreign/TestByteBuffer.java
+++ b/test/jdk/java/foreign/TestByteBuffer.java
@@ -28,11 +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.MemoryAddress;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.MemoryLayout.PathElement;
-import java.lang.foreign.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.lang.foreign.SequenceLayout;
import java.io.File;
@@ -63,7 +63,6 @@
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -186,8 +185,8 @@ static void checkBytes(MemorySegment base, SequenceLayout lay
@Test
public void testOffheap() {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(tuples, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(tuples, arena.scope());;
initTuples(segment, tuples.elementCount());
ByteBuffer bb = segment.asByteBuffer();
@@ -231,15 +230,15 @@ public void testChannel() throws Throwable {
@Test
public void testDefaultAccessModesMappedSegment() throws Throwable {
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena.scope());
assertFalse(segment.isReadOnly());
}
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 8L, session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 8L, arena.scope());
assertTrue(segment.isReadOnly());
}
}
@@ -250,18 +249,18 @@ public void testMappedSegment() throws Throwable {
f.createNewFile();
f.deleteOnExit();
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
//write to channel
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, tuples.byteSize(), session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, tuples.byteSize(), arena.scope());
initTuples(segment, tuples.elementCount());
segment.force();
}
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
//read from channel
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena.scope());
checkTuples(segment, segment.asByteBuffer(), tuples.elementCount());
}
}
@@ -272,11 +271,11 @@ public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws T
f.createNewFile();
f.deleteOnExit();
- try (MemorySession session = MemorySession.openConfined();
- FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, session);
+ Arena arena = Arena.openConfined();
+ try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena.scope());
assertTrue(segment.isMapped());
- segment.session().close();
+ arena.close();
mappedBufferOp.apply(segment);
}
}
@@ -291,10 +290,10 @@ public void testMappedSegmentOffset() throws Throwable {
// write one at a time
for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
//write to channel
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, i, tuples.byteSize(), session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, i, tuples.byteSize(), arena.scope());
initTuples(segment, 1);
segment.force();
}
@@ -302,10 +301,10 @@ public void testMappedSegmentOffset() throws Throwable {
// check one at a time
for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
//read from channel
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena.scope());
checkTuples(segment, segment.asByteBuffer(), 1);
}
}
@@ -323,9 +322,9 @@ public void testLargeMappedSegment() throws Throwable {
f.createNewFile();
f.deleteOnExit();
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, LARGE_SIZE, session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, LARGE_SIZE, arena.scope());
segment.isLoaded();
segment.load();
segment.isLoaded();
@@ -361,8 +360,8 @@ static void checkByteArrayAlignment(MemoryLayout layout) {
@Test(dataProvider = "bufferOps")
public void testScopedBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) {
Buffer bb;
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(bytes, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());;
bb = bufferFactory.apply(segment.asByteBuffer());
}
//outside of session!!
@@ -387,8 +386,8 @@ public void testScopedBuffer(Function bufferFactory, @NoInje
@Test(dataProvider = "bufferHandleOps")
public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
ByteBuffer bb;
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(bytes, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());;
bb = segment.asByteBuffer();
for (Map.Entry e : varHandleMembers(bb, bufferHandle).entrySet()) {
MethodHandle handle = e.getKey().bindTo(bufferHandle)
@@ -421,12 +420,12 @@ public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
@Test(dataProvider = "bufferOps")
public void testDirectBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(bytes, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());;
Buffer bb = bufferFactory.apply(segment.asByteBuffer());
assertTrue(bb.isDirect());
DirectBuffer directBuffer = ((DirectBuffer)bb);
- assertEquals(directBuffer.address(), segment.address().toRawLongValue());
+ assertEquals(directBuffer.address(), segment.address());
assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
assertTrue(directBuffer.cleaner() == null);
}
@@ -434,8 +433,8 @@ public void testDirectBuffer(Function bufferFactory, @NoInje
@Test(dataProvider="resizeOps")
public void testResizeOffheap(Consumer checker, Consumer initializer, SequenceLayout seq) {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(seq, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());;
initializer.accept(segment);
checker.accept(segment);
}
@@ -472,8 +471,8 @@ public void testResizeRoundtripHeap(Consumer checker, Consumer checker, Consumer initializer, SequenceLayout seq) {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(seq, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());;
initializer.accept(segment);
MemorySegment second = MemorySegment.ofBuffer(segment.asByteBuffer());
checker.accept(second);
@@ -483,8 +482,8 @@ public void testResizeRoundtripNative(Consumer checker, Consumer<
@Test(expectedExceptions = IllegalStateException.class)
public void testBufferOnClosedSession() {
MemorySegment leaked;
- try (MemorySession session = MemorySession.openConfined()) {
- leaked = MemorySegment.allocateNative(bytes, session);
+ try (Arena arena = Arena.openConfined()) {
+ leaked = MemorySegment.allocateNative(bytes, arena.scope());;
}
ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok
byteBuffer.get(); // should throw
@@ -492,7 +491,7 @@ public void testBufferOnClosedSession() {
@Test(expectedExceptions = IllegalStateException.class)
public void testTooBigForByteBuffer() {
- MemorySegment segment = MemorySegment.ofAddress(MemoryAddress.NULL, Integer.MAX_VALUE + 10L, MemorySession.openImplicit());
+ MemorySegment segment = MemorySegment.ofAddress(0, Integer.MAX_VALUE + 10L, SegmentScope.auto());
segment.asByteBuffer();
}
@@ -502,7 +501,7 @@ public void testBadMapNegativeSize() throws IOException {
f.createNewFile();
f.deleteOnExit();
try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, -1L, MemorySession.openImplicit());
+ fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, -1L, SegmentScope.auto());
}
}
@@ -512,7 +511,7 @@ public void testBadMapNegativeOffset() throws IOException {
f.createNewFile();
f.deleteOnExit();
try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- fileChannel.map(FileChannel.MapMode.READ_WRITE, -1L, 1L, MemorySession.openImplicit());
+ fileChannel.map(FileChannel.MapMode.READ_WRITE, -1L, 1L, SegmentScope.auto());
}
}
@@ -524,9 +523,9 @@ public void testMapOffset() throws IOException {
int SIZE = Byte.MAX_VALUE;
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SIZE, session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SIZE, arena.scope());
for (byte offset = 0; offset < SIZE; offset++) {
segment.set(JAVA_BYTE, offset, offset);
}
@@ -534,9 +533,9 @@ public void testMapOffset() throws IOException {
}
for (int offset = 0 ; offset < SIZE ; offset++) {
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, SIZE - offset, session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, SIZE - offset, arena.scope());
assertEquals(segment.get(JAVA_BYTE, 0), offset);
}
}
@@ -548,9 +547,9 @@ public void testMapZeroSize() throws IOException {
f.createNewFile();
f.deleteOnExit();
//RW
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, arena.scope());
assertEquals(segment.byteSize(), 0);
assertEquals(segment.isMapped(), true);
assertFalse(segment.isReadOnly());
@@ -560,9 +559,9 @@ public void testMapZeroSize() throws IOException {
segment.unload();
}
//RO
- try (MemorySession session = MemorySession.openConfined();
+ try (Arena arena = Arena.openConfined();
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
- MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 0L, session);
+ MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 0L, arena.scope());
assertEquals(segment.byteSize(), 0);
assertEquals(segment.isMapped(), true);
assertTrue(segment.isReadOnly());
@@ -577,7 +576,7 @@ public void testMapZeroSize() throws IOException {
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, MemorySession.openImplicit());
+ fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, SegmentScope.auto());
}
}
@@ -585,8 +584,8 @@ public void testMapCustomPath() throws IOException {
public void testCopyHeapToNative(Consumer checker, Consumer initializer, SequenceLayout seq) {
checkByteArrayAlignment(seq.elementLayout());
int bytes = (int)seq.byteSize();
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment nativeArray = MemorySegment.allocateNative(bytes, 1, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment nativeArray = MemorySegment.allocateNative(bytes, 1, arena.scope());;
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);
initializer.accept(heapArray);
nativeArray.copyFrom(heapArray);
@@ -598,8 +597,8 @@ public void testCopyHeapToNative(Consumer checker, Consumer checker, Consumer initializer, SequenceLayout seq) {
checkByteArrayAlignment(seq.elementLayout());
int bytes = (int)seq.byteSize();
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment nativeArray = MemorySegment.allocateNative(seq, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment nativeArray = MemorySegment.allocateNative(seq, arena.scope());;
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);
initializer.accept(nativeArray);
heapArray.copyFrom(nativeArray);
@@ -670,8 +669,8 @@ public void bufferProperties(ByteBuffer bb, Predicate _unused) {
@Test
public void testRoundTripAccess() {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment ms = MemorySegment.allocateNative(4, 1, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment ms = MemorySegment.allocateNative(4, 1, arena.scope());;
MemorySegment msNoAccess = ms.asReadOnly();
MemorySegment msRoundTrip = MemorySegment.ofBuffer(msNoAccess.asByteBuffer());
assertEquals(msNoAccess.isReadOnly(), msRoundTrip.isReadOnly());
@@ -680,23 +679,23 @@ public void testRoundTripAccess() {
@Test(expectedExceptions = IllegalStateException.class)
public void testDeadAccessOnClosedBufferSegment() {
- MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, MemorySession.openConfined());
+ Arena arena = Arena.openConfined();
+ MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, arena.scope());
MemorySegment s2 = MemorySegment.ofBuffer(s1.asByteBuffer());
// memory freed
- s1.session().close();
+ arena.close();
s2.set(JAVA_INT, 0, 10); // Dead access!
}
- @Test(dataProvider = "allSessions")
- public void testIOOnSegmentBuffer(Supplier sessionSupplier) throws IOException {
+ @Test(dataProvider = "closeableArenas")
+ public void closeableArenas(Supplier arenaSupplier) throws IOException {
File tmp = File.createTempFile("tmp", "txt");
tmp.deleteOnExit();
- MemorySession session;
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE) ;
- MemorySession scp = closeableSessionOrNull(session = sessionSupplier.get())) {
- MemorySegment segment = MemorySegment.allocateNative(10, 1, session);
+ Arena arena = arenaSupplier.get()) {
+ MemorySegment segment = MemorySegment.allocateNative(10, 1, arena.scope());;
for (int i = 0; i < 10; i++) {
segment.set(JAVA_BYTE, i, (byte) i);
}
@@ -711,17 +710,18 @@ public void testIOOnSegmentBuffer(Supplier sessionSupplier) throw
static final Class ISE = IllegalStateException.class;
- @Test(dataProvider = "closeableSessions")
- public void testIOOnClosedSegmentBuffer(Supplier sessionSupplier) throws IOException {
+ @Test(dataProvider = "closeableArenas")
+ public void testIOOnClosedSegmentBuffer(Supplier arenaSupplier) throws IOException {
File tmp = File.createTempFile("tmp", "txt");
tmp.deleteOnExit();
+ Arena arena = arenaSupplier.get();
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- MemorySegment segment = MemorySegment.allocateNative(10, sessionSupplier.get());
+ MemorySegment segment = MemorySegment.allocateNative(10, arena.scope());
for (int i = 0; i < 10; i++) {
segment.set(JAVA_BYTE, i, (byte) i);
}
ByteBuffer bb = segment.asByteBuffer();
- segment.session().close();
+ arena.close();
assertThrows(ISE, () -> channel.read(bb));
assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}));
assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}, 0, 1));
@@ -733,8 +733,8 @@ public void testIOOnClosedSegmentBuffer(Supplier sessionSupplier)
@Test
public void buffersAndArraysFromSlices() {
- try (MemorySession session = MemorySession.openShared()) {
- MemorySegment segment = MemorySegment.allocateNative(16, session);
+ try (Arena arena = Arena.openShared()) {
+ MemorySegment segment = MemorySegment.allocateNative(16, arena.scope());;
int newSize = 8;
var slice = segment.asSlice(4, newSize);
@@ -751,8 +751,8 @@ public void buffersAndArraysFromSlices() {
@Test
public void viewsFromSharedSegment() {
- try (MemorySession session = MemorySession.openShared()) {
- MemorySegment segment = MemorySegment.allocateNative(16, session);
+ try (Arena arena = Arena.openShared()) {
+ MemorySegment segment = MemorySegment.allocateNative(16, arena.scope());;
var byteBuffer = segment.asByteBuffer();
byteBuffer.asReadOnlyBuffer();
byteBuffer.slice(0, 8);
@@ -762,33 +762,20 @@ public void viewsFromSharedSegment() {
@DataProvider(name = "segments")
public static Object[][] segments() throws Throwable {
return new Object[][] {
- { (Supplier) () -> MemorySegment.allocateNative(16, MemorySession.openImplicit()) },
- { (Supplier) () -> MemorySegment.allocateNative(16, MemorySession.openConfined()) },
+ { (Supplier) () -> MemorySegment.allocateNative(16, SegmentScope.auto()) },
+ { (Supplier) () -> MemorySegment.allocateNative(16, Arena.openConfined().scope()) },
{ (Supplier) () -> MemorySegment.ofArray(new byte[16]) }
};
}
- @DataProvider(name = "closeableSessions")
- public static Object[][] closeableSessions() {
+ @DataProvider(name = "closeableArenas")
+ public static Object[][] closeableArenas() {
return new Object[][] {
- { (Supplier) () -> MemorySession.openShared() },
- { (Supplier) () -> MemorySession.openConfined() },
- { (Supplier) () -> MemorySession.openShared(Cleaner.create()) },
- { (Supplier) () -> MemorySession.openConfined(Cleaner.create()) },
+ { (Supplier) Arena::openConfined },
+ { (Supplier) Arena::openShared },
};
}
- @DataProvider(name = "allSessions")
- public static Object[][] allSessions() {
- return Stream.of(new Object[][] { { (Supplier) MemorySession::global} }, closeableSessions())
- .flatMap(Arrays::stream)
- .toArray(Object[][]::new);
- }
-
- static MemorySession closeableSessionOrNull(MemorySession session) {
- return session.isCloseable() ? session : null;
- }
-
@DataProvider(name = "bufferOps")
public static Object[][] bufferOps() throws Throwable {
List args = new ArrayList<>();
diff --git a/test/jdk/java/foreign/TestClassLoaderFindNative.java b/test/jdk/java/foreign/TestClassLoaderFindNative.java
index d83ca5bea9b17..b462180b135cb 100644
--- a/test/jdk/java/foreign/TestClassLoaderFindNative.java
+++ b/test/jdk/java/foreign/TestClassLoaderFindNative.java
@@ -29,7 +29,7 @@
*/
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import org.testng.annotations.Test;
@@ -49,20 +49,20 @@ public class TestClassLoaderFindNative {
@Test
public void testSimpleLookup() {
- assertFalse(SymbolLookup.loaderLookup().lookup("f").isEmpty());
+ assertFalse(SymbolLookup.loaderLookup().find("f").isEmpty());
}
@Test
public void testInvalidSymbolLookup() {
- assertTrue(SymbolLookup.loaderLookup().lookup("nonExistent").isEmpty());
+ assertTrue(SymbolLookup.loaderLookup().find("nonExistent").isEmpty());
}
@Test
public void testVariableSymbolLookup() {
MemorySegment segment = MemorySegment.ofAddress(
- SymbolLookup.loaderLookup().lookup("c").get().address(),
+ SymbolLookup.loaderLookup().find("c").get().address(),
ValueLayout.JAVA_INT.byteSize(),
- MemorySession.global());
+ SegmentScope.global());
assertEquals(segment.get(JAVA_BYTE, 0), 42);
}
}
diff --git a/test/jdk/java/foreign/TestDowncallBase.java b/test/jdk/java/foreign/TestDowncallBase.java
index 3e992228a6a7d..14d8928afc895 100644
--- a/test/jdk/java/foreign/TestDowncallBase.java
+++ b/test/jdk/java/foreign/TestDowncallBase.java
@@ -22,9 +22,9 @@
*
*/
-import java.lang.foreign.Addressable;
import java.lang.foreign.Linker;
import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.MemorySegment;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.SegmentAllocator;
import java.lang.invoke.MethodHandle;
@@ -36,7 +36,7 @@ public class TestDowncallBase extends CallGeneratorHelper {
static Linker LINKER = Linker.nativeLinker();
- Object doCall(Addressable symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable {
+ Object doCall(MemorySegment symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable {
MethodHandle mh = downcallHandle(LINKER, symbol, allocator, descriptor);
Object res = mh.invokeWithArguments(args);
return res;
diff --git a/test/jdk/java/foreign/TestDowncallScope.java b/test/jdk/java/foreign/TestDowncallScope.java
index 67e63273e0cc3..1d586274ff142 100644
--- a/test/jdk/java/foreign/TestDowncallScope.java
+++ b/test/jdk/java/foreign/TestDowncallScope.java
@@ -38,11 +38,10 @@
import org.testng.annotations.Test;
-import java.lang.foreign.Addressable;
+import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.foreign.SegmentAllocator;
import java.util.ArrayList;
import java.util.List;
@@ -61,20 +60,20 @@ public void testDowncall(int count, String fName, CallGeneratorHelper.Ret ret,
List paramTypes,
List fields) throws Throwable {
List> checks = new ArrayList<>();
- Addressable addr = findNativeOrThrow(fName);
+ MemorySegment addr = findNativeOrThrow(fName);
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
Object[] args = makeArgs(paramTypes, fields, checks);
- try (MemorySession session = MemorySession.openShared()) {
+ try (Arena arena = Arena.openShared()) {
boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false);
SegmentAllocator allocator = needsScope ?
- SegmentAllocator.newNativeArena(session) :
+ SegmentAllocator.nativeAllocator(arena.scope()) :
THROWING_ALLOCATOR;
Object res = doCall(addr, allocator, descriptor, args);
if (ret == CallGeneratorHelper.Ret.NON_VOID) {
checks.forEach(c -> c.accept(res));
if (needsScope) {
// check that return struct has indeed been allocated in the native scope
- assertEquals(((MemorySegment)res).session(), session);
+ assertEquals(((MemorySegment)res).scope(), arena.scope());
}
}
}
diff --git a/test/jdk/java/foreign/TestDowncallStack.java b/test/jdk/java/foreign/TestDowncallStack.java
index abf7d32b968c2..1426604a93102 100644
--- a/test/jdk/java/foreign/TestDowncallStack.java
+++ b/test/jdk/java/foreign/TestDowncallStack.java
@@ -34,11 +34,10 @@
import org.testng.annotations.Test;
-import java.lang.foreign.Addressable;
+import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.foreign.SegmentAllocator;
import java.util.ArrayList;
import java.util.List;
@@ -57,20 +56,20 @@ public void testDowncallStack(int count, String fName, CallGeneratorHelper.Ret r
List paramTypes,
List fields) throws Throwable {
List> checks = new ArrayList<>();
- Addressable addr = findNativeOrThrow("s" + fName);
+ MemorySegment addr = findNativeOrThrow("s" + fName);
FunctionDescriptor descriptor = functionStack(ret, paramTypes, fields);
Object[] args = makeArgsStack(paramTypes, fields, checks);
- try (MemorySession session = MemorySession.openShared()) {
+ try (Arena arena = Arena.openShared()) {
boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false);
SegmentAllocator allocator = needsScope ?
- SegmentAllocator.newNativeArena(session) :
+ SegmentAllocator.nativeAllocator(arena.scope()) :
THROWING_ALLOCATOR;
Object res = doCall(addr, allocator, descriptor, args);
if (ret == CallGeneratorHelper.Ret.NON_VOID) {
checks.forEach(c -> c.accept(res));
if (needsScope) {
// check that return struct has indeed been allocated in the native scope
- assertEquals(((MemorySegment)res).session(), session);
+ assertEquals(((MemorySegment)res).scope(), arena.scope());
}
}
}
diff --git a/test/jdk/java/foreign/TestFallbackLookup.java b/test/jdk/java/foreign/TestFallbackLookup.java
index d129db228c59f..71363590b9a30 100644
--- a/test/jdk/java/foreign/TestFallbackLookup.java
+++ b/test/jdk/java/foreign/TestFallbackLookup.java
@@ -38,6 +38,6 @@ public class TestFallbackLookup {
void testBadSystemLookupRequest() {
// we request a Linker, forcing OS name to be "Windows". This should trigger an exception when
// attempting to load a non-existent ucrtbase.dll. Make sure that no error is generated at this stage.
- assertTrue(Linker.nativeLinker().defaultLookup().lookup("nonExistentSymbol").isEmpty());
+ assertTrue(Linker.nativeLinker().defaultLookup().find("nonExistentSymbol").isEmpty());
}
}
diff --git a/test/jdk/java/foreign/TestFree.java b/test/jdk/java/foreign/TestFree.java
index 2a5dcac2796f7..a61499a2194fa 100644
--- a/test/jdk/java/foreign/TestFree.java
+++ b/test/jdk/java/foreign/TestFree.java
@@ -30,25 +30,17 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestFree
*/
-import java.lang.foreign.MemoryAddress;
-import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import static org.testng.Assert.assertEquals;
public class TestFree extends NativeTestHelper {
- private static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements) {
- return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), MemorySession.global());
- }
-
public void test() throws Throwable {
String str = "hello world";
- MemoryAddress addr = allocateMemory(str.length() + 1);
- MemorySegment seg = asArray(addr, C_CHAR, str.length() + 1);
- seg.copyFrom(MemorySegment.ofArray(str.getBytes()));
- seg.set(C_CHAR, str.length(), (byte)0);
- assertEquals(str, seg.getUtf8String(0));
+ MemorySegment addr = allocateMemory(str.length() + 1);
+ addr.copyFrom(MemorySegment.ofArray(str.getBytes()));
+ addr.set(C_CHAR, str.length(), (byte)0);
+ assertEquals(str, addr.getUtf8String(0));
freeMemory(addr);
}
}
diff --git a/test/jdk/java/foreign/TestFunctionDescriptor.java b/test/jdk/java/foreign/TestFunctionDescriptor.java
index 2438ce23c4837..915c97ad50725 100644
--- a/test/jdk/java/foreign/TestFunctionDescriptor.java
+++ b/test/jdk/java/foreign/TestFunctionDescriptor.java
@@ -31,6 +31,8 @@
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Optional;
import org.testng.annotations.Test;
@@ -98,10 +100,25 @@ public void testDropReturnLayout() {
@Test
public void testEquals() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, C_INT);
- FunctionDescriptor fd_va1 = FunctionDescriptor.of(C_INT).asVariadic(C_INT, C_INT);
- FunctionDescriptor fd_va2 = FunctionDescriptor.of(C_INT, C_INT).asVariadic(C_INT);
assertEquals(fd, fd);
- assertNotEquals(fd, fd_va1);
- assertNotEquals(fd, fd_va2);
+ }
+
+ @Test
+ public void testCarrierMethodType() {
+ FunctionDescriptor fd = FunctionDescriptor.of(C_INT,
+ C_INT,
+ MemoryLayout.structLayout(C_INT, C_INT));
+ MethodType cmt = fd.toMethodType();
+ assertEquals(cmt, MethodType.methodType(int.class, int.class, MemorySegment.class));
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testBadCarrierMethodType() {
+ FunctionDescriptor fd = FunctionDescriptor.of(C_INT,
+ C_INT,
+ MemoryLayout.structLayout(C_INT, C_INT),
+ MemoryLayout.sequenceLayout(3, C_INT),
+ MemoryLayout.paddingLayout(32));
+ fd.toMethodType(); // should throw
}
}
diff --git a/test/jdk/java/foreign/TestHandshake.java b/test/jdk/java/foreign/TestHandshake.java
index 8d33b5b64ce79..519739cb1eb14 100644
--- a/test/jdk/java/foreign/TestHandshake.java
+++ b/test/jdk/java/foreign/TestHandshake.java
@@ -32,8 +32,8 @@
* @run testng/othervm -XX:-TieredCompilation TestHandshake
*/
+import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
@@ -67,8 +67,8 @@ public class TestHandshake {
@Test(dataProvider = "accessors")
public void testHandshake(String testName, AccessorFactory accessorFactory) throws InterruptedException {
for (int it = 0 ; it < ITERATIONS ; it++) {
- MemorySession session = MemorySession.openShared();
- MemorySegment segment = MemorySegment.allocateNative(SEGMENT_SIZE, 1, session);
+ Arena arena = Arena.openShared();
+ MemorySegment segment = MemorySegment.allocateNative(SEGMENT_SIZE, 1, arena.scope());
System.out.println("ITERATION " + it);
ExecutorService accessExecutor = Executors.newCachedThreadPool();
start.set(System.currentTimeMillis());
@@ -79,10 +79,10 @@ public void testHandshake(String testName, AccessorFactory accessorFactory) thro
int delay = ThreadLocalRandom.current().nextInt(MAX_DELAY_MILLIS);
System.out.println("Starting handshaker with delay set to " + delay + " millis");
Thread.sleep(delay);
- accessExecutor.execute(new Handshaker(session));
+ accessExecutor.execute(new Handshaker(arena));
accessExecutor.shutdown();
assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS));
- assertTrue(!segment.session().isAlive());
+ assertTrue(!segment.scope().isAlive());
}
}
@@ -99,7 +99,7 @@ static abstract class AbstractSegmentAccessor implements Runnable {
@Override
public final void run() {
start("\"Accessor #\" + id");
- outer: while (segment.session().isAlive()) {
+ outer: while (segment.scope().isAlive()) {
try {
doAccess();
} catch (IllegalStateException ex) {
@@ -193,7 +193,7 @@ static class SegmentMismatchAccessor extends AbstractSegmentAccessor {
SegmentMismatchAccessor(int id, MemorySegment segment) {
super(id, segment);
- this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.session());
+ this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.scope());
copy.copyFrom(segment);
copy.set(JAVA_BYTE, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42);
}
@@ -238,10 +238,10 @@ public void doAccess() {
static class Handshaker implements Runnable {
- final MemorySession session;
+ final Arena arena;
- Handshaker(MemorySession session) {
- this.session = session;
+ Handshaker(Arena arena) {
+ this.arena = arena;
}
@Override
@@ -249,7 +249,7 @@ public void run() {
start("Handshaker");
while (true) {
try {
- session.close();
+ arena.close();
break;
} catch (IllegalStateException ex) {
Thread.onSpinWait();
diff --git a/test/jdk/java/foreign/TestHeapAlignment.java b/test/jdk/java/foreign/TestHeapAlignment.java
index 91a154538166e..eff8dca98020e 100644
--- a/test/jdk/java/foreign/TestHeapAlignment.java
+++ b/test/jdk/java/foreign/TestHeapAlignment.java
@@ -29,10 +29,9 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestHeapAlignment
*/
-import java.lang.foreign.MemoryAddress;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
+import java.lang.foreign.SegmentScope;
import java.lang.foreign.ValueLayout;
import java.util.ArrayList;
import java.util.List;
@@ -101,7 +100,7 @@ enum SegmentAndAlignment {
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, MemorySession.openImplicit()), -1);
+ NATIVE(MemorySegment.allocateNative(8, SegmentScope.auto()), -1);
final MemorySegment segment;
final int align;
@@ -124,7 +123,7 @@ public static Object[][] layouts() {
layouts.add(new Object[] { testCase.segment, testCase.align, 42f, new float[]{42}, JAVA_FLOAT_ALIGNED, (Function)MemorySegment::ofArray });
layouts.add(new Object[] { testCase.segment, testCase.align, 42L, new long[]{42}, JAVA_LONG_ALIGNED, (Function)MemorySegment::ofArray });
layouts.add(new Object[] { testCase.segment, testCase.align, 42d, new double[]{42}, JAVA_DOUBLE_ALIGNED, (Function)MemorySegment::ofArray });
- layouts.add(new Object[] { testCase.segment, testCase.align, MemoryAddress.ofLong(42), null, ADDRESS_ALIGNED, null });
+ layouts.add(new Object[] { testCase.segment, testCase.align, MemorySegment.ofAddress(42), null, ADDRESS_ALIGNED, null });
}
return layouts.toArray(new Object[0][]);
}
diff --git a/test/jdk/java/foreign/TestIllegalLink.java b/test/jdk/java/foreign/TestIllegalLink.java
index 3219d15015820..670ac641eb2b8 100644
--- a/test/jdk/java/foreign/TestIllegalLink.java
+++ b/test/jdk/java/foreign/TestIllegalLink.java
@@ -29,11 +29,12 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestIllegalLink
*/
-import java.lang.foreign.Addressable;
import java.lang.foreign.Linker;
import java.lang.foreign.FunctionDescriptor;
-import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemorySegment;
import java.lang.foreign.MemoryLayout;
+import java.nio.ByteOrder;
+
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -42,7 +43,7 @@
public class TestIllegalLink extends NativeTestHelper {
- private static final Addressable DUMMY_TARGET = MemoryAddress.ofLong(1);
+ private static final MemorySegment DUMMY_TARGET = MemorySegment.ofAddress(1);
private static final Linker ABI = Linker.nativeLinker();
@Test(dataProvider = "types")
@@ -51,7 +52,8 @@ public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMe
ABI.downcallHandle(DUMMY_TARGET, desc);
fail("Expected IllegalArgumentException was not thrown");
} catch (IllegalArgumentException e) {
- assertTrue(e.getMessage().contains(expectedExceptionMessage));
+ assertTrue(e.getMessage().contains(expectedExceptionMessage),
+ e.getMessage() + " != " + expectedExceptionMessage);
}
}
@@ -59,12 +61,12 @@ public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMe
public static Object[][] types() {
return new Object[][]{
{
- FunctionDescriptor.of(MemoryLayout.paddingLayout(64)),
- "Unsupported layout: x64"
+ FunctionDescriptor.of(MemoryLayout.paddingLayout(64)),
+ "Unsupported layout: x64"
},
{
- FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(64)),
- "Unsupported layout: x64"
+ FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(64)),
+ "Unsupported layout: x64"
},
{
FunctionDescriptor.of(MemoryLayout.sequenceLayout(2, C_INT)),
@@ -74,6 +76,42 @@ public static Object[][] types() {
FunctionDescriptor.ofVoid(MemoryLayout.sequenceLayout(2, C_INT)),
"Unsupported layout: [2:i32]"
},
+ {
+ FunctionDescriptor.ofVoid(C_INT.withBitAlignment(16)),
+ "Layout bit alignment must be natural alignment"
+ },
+ {
+ FunctionDescriptor.ofVoid(C_POINTER.withBitAlignment(16)),
+ "Layout bit alignment must be natural alignment"
+ },
+ {
+ FunctionDescriptor.ofVoid(MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()).withBitAlignment(32)),
+ "Layout bit alignment must be natural alignment"
+ },
+ {
+ FunctionDescriptor.ofVoid(MemoryLayout.structLayout(
+ C_CHAR.withName("x").withBitAlignment(8),
+ C_SHORT.withName("y").withBitAlignment(8),
+ C_INT.withName("z").withBitAlignment(8)
+ ).withBitAlignment(8)),
+ "Layout bit alignment must be natural alignment"
+ },
+ {
+ FunctionDescriptor.ofVoid(MemoryLayout.structLayout(
+ MemoryLayout.structLayout(
+ C_CHAR.withName("x").withBitAlignment(8),
+ C_SHORT.withName("y").withBitAlignment(8),
+ C_INT.withName("z").withBitAlignment(8)
+ ))),
+ "Layout bit alignment must be natural alignment"
+ },
+ {
+ FunctionDescriptor.ofVoid(MemoryLayout.structLayout(
+ MemoryLayout.sequenceLayout(
+ C_INT.withBitAlignment(8)
+ ))),
+ "Layout bit alignment must be natural alignment"
+ },
};
}
diff --git a/test/jdk/java/foreign/TestIntrinsics.java b/test/jdk/java/foreign/TestIntrinsics.java
index 60ed3faf757e6..38c742015bb62 100644
--- a/test/jdk/java/foreign/TestIntrinsics.java
+++ b/test/jdk/java/foreign/TestIntrinsics.java
@@ -32,18 +32,18 @@
* TestIntrinsics
*/
-import java.lang.foreign.Addressable;
import java.lang.foreign.Linker;
import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.MemorySegment;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.List;
import java.lang.foreign.MemoryLayout;
import org.testng.annotations.*;
+import static java.lang.foreign.Linker.Option.firstVariadicArg;
import static java.lang.invoke.MethodType.methodType;
import static java.lang.foreign.ValueLayout.JAVA_CHAR;
import static org.testng.Assert.assertEquals;
@@ -84,8 +84,7 @@ interface AddIdentity {
}
AddIdentity addIdentity = (name, carrier, layout, arg) -> {
- Addressable ma = findNativeOrThrow(name);
- MethodType mt = methodType(carrier, carrier);
+ MemorySegment ma = findNativeOrThrow(name);
FunctionDescriptor fd = FunctionDescriptor.of(layout, layout);
tests.add(abi.downcallHandle(ma, fd), arg, arg);
@@ -93,8 +92,7 @@ interface AddIdentity {
};
{ // empty
- Addressable ma = findNativeOrThrow("empty");
- MethodType mt = methodType(void.class);
+ MemorySegment ma = findNativeOrThrow("empty");
FunctionDescriptor fd = FunctionDescriptor.ofVoid();
tests.add(abi.downcallHandle(ma, fd), null);
}
@@ -108,21 +106,18 @@ interface AddIdentity {
addIdentity.add("identity_double", double.class, C_DOUBLE, 10D);
{ // identity_va
- Addressable ma = findNativeOrThrow("identity_va");
- MethodType mt = methodType(int.class, int.class, double.class, int.class, float.class, long.class);
- FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT).asVariadic(C_DOUBLE, C_INT, C_FLOAT, C_LONG_LONG);
- tests.add(abi.downcallHandle(ma, fd), 1, 1, 10D, 2, 3F, 4L);
+ MemorySegment ma = findNativeOrThrow("identity_va");
+ FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT,
+ C_DOUBLE, C_INT, C_FLOAT, C_LONG_LONG);
+ tests.add(abi.downcallHandle(ma, fd, firstVariadicArg(1)), 1, 1, 10D, 2, 3F, 4L);
}
{ // high_arity
- MethodType baseMT = methodType(void.class, int.class, double.class, long.class, float.class, byte.class,
- short.class, char.class);
FunctionDescriptor baseFD = FunctionDescriptor.ofVoid(C_INT, C_DOUBLE, C_LONG_LONG, C_FLOAT, C_CHAR,
C_SHORT, JAVA_CHAR);
Object[] args = {1, 10D, 2L, 3F, (byte) 0, (short) 13, 'a'};
for (int i = 0; i < args.length; i++) {
- Addressable ma = findNativeOrThrow("invoke_high_arity" + i);
- MethodType mt = baseMT.changeReturnType(baseMT.parameterType(i));
+ MemorySegment ma = findNativeOrThrow("invoke_high_arity" + i);
FunctionDescriptor fd = baseFD.changeReturnLayout(baseFD.argumentLayouts().get(i));
Object expected = args[i];
tests.add(abi.downcallHandle(ma, fd), expected, args);
diff --git a/test/jdk/java/foreign/TestLargeSegmentCopy.java b/test/jdk/java/foreign/TestLargeSegmentCopy.java
new file mode 100644
index 0000000000000..0ed9b3971afbb
--- /dev/null
+++ b/test/jdk/java/foreign/TestLargeSegmentCopy.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ *
+ * 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
+ * @bug 8292851
+ * @run testng/othervm -Xmx4G TestLargeSegmentCopy
+ */
+
+import org.testng.annotations.Test;
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.MemorySegment;
+
+import static java.lang.foreign.ValueLayout.JAVA_LONG;
+
+public class TestLargeSegmentCopy {
+
+ @Test
+ public void testLargeSegmentCopy() {
+ // Make sure the byte size is bigger than Integer.MAX_VALUE
+ 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());
+ // Should not throw an exception or error
+ MemorySegment.copy(segment, JAVA_LONG, 0, array, 0, longArrayLength);
+ // Should not throw an exception or error
+ MemorySegment.copy(array,0, segment, JAVA_LONG, 0, longArrayLength);
+ }
+
+ }
+
+}
diff --git a/test/jdk/java/foreign/TestLayoutEquality.java b/test/jdk/java/foreign/TestLayoutEquality.java
index 66c8a8e3fc87b..e07638e5078f0 100644
--- a/test/jdk/java/foreign/TestLayoutEquality.java
+++ b/test/jdk/java/foreign/TestLayoutEquality.java
@@ -29,7 +29,7 @@
* @run testng TestLayoutEquality
*/
-import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemoryLayout;
import java.lang.foreign.ValueLayout;
import jdk.internal.foreign.PlatformLayouts;
import org.testng.annotations.DataProvider;
@@ -39,24 +39,17 @@
import java.util.ArrayList;
import java.util.List;
-import static java.lang.foreign.ValueLayout.ADDRESS;
-import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN;
-import static java.lang.foreign.ValueLayout.JAVA_BYTE;
-import static java.lang.foreign.ValueLayout.JAVA_CHAR;
-import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
-import static java.lang.foreign.ValueLayout.JAVA_FLOAT;
-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 org.testng.Assert.*;
public class TestLayoutEquality {
@Test(dataProvider = "layoutConstants")
public void testReconstructedEquality(ValueLayout layout) {
- ValueLayout newLayout = valueLayoutForCarrier(layout.carrier());
+ ValueLayout newLayout = MemoryLayout.valueLayout(layout.carrier(), layout.order());
newLayout = newLayout.withBitAlignment(layout.bitAlignment());
- newLayout = newLayout.withOrder(layout.order());
+ if (layout instanceof ValueLayout.OfAddress addressLayout && addressLayout.isUnbounded()) {
+ newLayout = ((ValueLayout.OfAddress)newLayout).asUnbounded();
+ }
// properties should be equal
assertEquals(newLayout.bitSize(), layout.bitSize());
@@ -84,28 +77,4 @@ private static void addLayoutConstants(List testValues, Class> cl
testValues.add((ValueLayout) f.get(null));
}
}
-
- static ValueLayout valueLayoutForCarrier(Class> carrier) {
- if (carrier == boolean.class) {
- return JAVA_BOOLEAN;
- } else if (carrier == char.class) {
- return JAVA_CHAR;
- } else if (carrier == byte.class) {
- return JAVA_BYTE;
- } else if (carrier == short.class) {
- return JAVA_SHORT;
- } else if (carrier == int.class) {
- return JAVA_INT;
- } else if (carrier == long.class) {
- return JAVA_LONG;
- } else if (carrier == float.class) {
- return JAVA_FLOAT;
- } else if (carrier == double.class) {
- return JAVA_DOUBLE;
- } else if (carrier == MemoryAddress.class) {
- return ADDRESS;
- } else {
- throw new UnsupportedOperationException();
- }
- }
}
diff --git a/test/jdk/java/foreign/TestLayoutPaths.java b/test/jdk/java/foreign/TestLayoutPaths.java
index d3daa3ec3edb8..59663af82003d 100644
--- a/test/jdk/java/foreign/TestLayoutPaths.java
+++ b/test/jdk/java/foreign/TestLayoutPaths.java
@@ -28,11 +28,11 @@
* @run testng TestLayoutPaths
*/
+import java.lang.foreign.Arena;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemoryLayout.PathElement;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.ValueLayout;
@@ -433,10 +433,10 @@ public void testSliceHandle(MemoryLayout layout, PathElement[] pathElements, lon
MethodHandle sliceHandle = layout.sliceHandle(pathElements);
sliceHandle = sliceHandle.asSpreader(long[].class, indexes.length);
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(layout, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());
MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, indexes);
- assertEquals(slice.address().toRawLongValue() - segment.address().toRawLongValue(), expectedBitOffset / 8);
+ assertEquals(slice.address() - segment.address(), expectedBitOffset / 8);
assertEquals(slice.byteSize(), selected.byteSize());
}
}
@@ -468,8 +468,8 @@ public void testSliceHandleUOEInvalidOffsetLate() throws Throwable {
return;
}
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(layout, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());
try {
sliceHandle.invokeExact(segment, 1); // should work
diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java
index 843c88d5e3662..e09eaab508a86 100644
--- a/test/jdk/java/foreign/TestLayouts.java
+++ b/test/jdk/java/foreign/TestLayouts.java
@@ -52,8 +52,8 @@ public void testBadLayoutAlignment(MemoryLayout layout, long alignment) {
@Test
public void testIndexedSequencePath() {
MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(seq, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());;
VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement());
// init segment
for (int i = 0 ; i < 10 ; i++) {
@@ -138,10 +138,15 @@ public void testSequenceBadCount() {
@Test(dataProvider = "basicLayouts")
public void testSequenceInferredCount(MemoryLayout layout) {
- assertEquals(MemoryLayout.sequenceLayout(-1, layout),
+ assertEquals(MemoryLayout.sequenceLayout(layout),
MemoryLayout.sequenceLayout(Long.MAX_VALUE / layout.bitSize(), layout));
}
+ public void testSequenceNegativeElementCount() {
+ assertThrows(IllegalArgumentException.class, // negative
+ () -> MemoryLayout.sequenceLayout(-1, JAVA_SHORT));
+ }
+
@Test
public void testSequenceOverflow() {
assertThrows(IllegalArgumentException.class, // negative
@@ -163,7 +168,7 @@ public void testStructOverflow() {
@Test(dataProvider = "layoutKinds")
public void testPadding(LayoutKind kind) {
- assertEquals(kind == LayoutKind.PADDING, kind.layout.isPadding());
+ assertEquals(kind == LayoutKind.PADDING, kind.layout instanceof PaddingLayout);
}
@Test(dataProvider="layoutsAndAlignments")
diff --git a/test/jdk/java/foreign/TestLinker.java b/test/jdk/java/foreign/TestLinker.java
new file mode 100644
index 0000000000000..8077e3ef8418a
--- /dev/null
+++ b/test/jdk/java/foreign/TestLinker.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ * 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 TestLinker
+ */
+
+import org.testng.annotations.Test;
+
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.Linker;
+import java.lang.invoke.MethodHandle;
+
+import static org.testng.Assert.assertNotSame;
+
+public class TestLinker extends NativeTestHelper {
+
+ @Test
+ public void testLinkerOptionsCache() {
+ Linker linker = Linker.nativeLinker();
+ FunctionDescriptor descriptor = FunctionDescriptor.ofVoid(C_INT, C_INT);
+ MethodHandle mh1 = linker.downcallHandle(descriptor);
+ MethodHandle mh2 = linker.downcallHandle(descriptor, Linker.Option.firstVariadicArg(1));
+ // assert that these are 2 distinct link request. No caching allowed
+ assertNotSame(mh1, mh2);
+ }
+
+}
diff --git a/test/jdk/java/foreign/TestMemoryAccess.java b/test/jdk/java/foreign/TestMemoryAccess.java
index 1fa39a40fd85c..7028635b68253 100644
--- a/test/jdk/java/foreign/TestMemoryAccess.java
+++ b/test/jdk/java/foreign/TestMemoryAccess.java
@@ -30,12 +30,11 @@
* @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.MemoryAddress;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemoryLayout.PathElement;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.ValueLayout;
@@ -92,8 +91,8 @@ public void testPaddedArrayAccessByIndexSeq(Function viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) {
MemorySegment outer_segment;
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout, session));
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout, arena.scope()));
boolean isRO = segment.isReadOnly();
try {
checker.check(handle, segment);
@@ -124,8 +123,8 @@ private void testAccessInternal(Function viewFacto
private void testArrayAccessInternal(Function viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) {
MemorySegment outer_segment;
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, session));
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, arena.scope()));
boolean isRO = segment.isReadOnly();
try {
for (int i = 0; i < seq.elementCount(); i++) {
@@ -193,8 +192,8 @@ public void testPaddedMatrixAccessByIndexSeq(Function viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) {
MemorySegment outer_segment;
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, session));
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, arena.scope()));
boolean isRO = segment.isReadOnly();
try {
for (int i = 0; i < seq.elementCount(); i++) {
@@ -465,8 +464,8 @@ interface MatrixChecker {
};
MatrixChecker ADDR = (handle, segment, r, c) -> {
- handle.set(segment, r, c, MemoryAddress.ofLong(r + c));
- assertEquals(MemoryAddress.ofLong(r + c), (MemoryAddress)handle.get(segment, r, c));
+ handle.set(segment, r, c, MemorySegment.ofAddress(r + c));
+ assertEquals(MemorySegment.ofAddress(r + c), (MemorySegment) handle.get(segment, r, c));
};
MatrixChecker FLOAT = (handle, segment, r, c) -> {
diff --git a/test/jdk/java/foreign/TestMemoryAccessInstance.java b/test/jdk/java/foreign/TestMemoryAccessInstance.java
index c75aeb4597885..d2a8ba14f9c7d 100644
--- a/test/jdk/java/foreign/TestMemoryAccessInstance.java
+++ b/test/jdk/java/foreign/TestMemoryAccessInstance.java
@@ -27,9 +27,8 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance
*/
-import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.foreign.ValueLayout;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -80,8 +79,8 @@ interface BufferSetter {
}
void test() {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(128, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(128, arena.scope());;
ByteBuffer buffer = segment.asByteBuffer();
T t = transform.apply(segment);
segmentSetter.set(t, layout, 8, value);
@@ -93,8 +92,8 @@ void test() {
@SuppressWarnings("unchecked")
void testHyperAligned() {
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(64, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(64, arena.scope());;
T t = transform.apply(segment);
L alignedLayout = (L)layout.withBitAlignment(layout.byteSize() * 8 * 2);
try {
@@ -117,12 +116,6 @@ static Accessor ofSegment(L layo
BufferGetter bufferGetter, BufferSetter bufferSetter) {
return new Accessor<>(Function.identity(), layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter);
}
-
- static Accessor ofAddress(L layout, X value,
- SegmentGetter segmentGetter, SegmentSetter segmentSetter,
- BufferGetter bufferGetter, BufferSetter bufferSetter) {
- return new Accessor<>(MemorySegment::address, layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter);
- }
}
@Test(dataProvider = "segmentAccessors")
@@ -130,11 +123,6 @@ public void testSegmentAccess(String testName, Accessor, ?, ?> accessor) {
accessor.test();
}
- @Test(dataProvider = "addressAccessors")
- public void testAddressAccess(String testName, Accessor, ?, ?> accessor) {
- accessor.test();
- }
-
@Test(dataProvider = "segmentAccessors")
public void testSegmentAccessHyper(String testName, Accessor, ?, ?> accessor) {
if (testName.contains("index")) {
@@ -144,15 +132,6 @@ public void testSegmentAccessHyper(String testName, Accessor, ?, ?> accessor)
}
}
- @Test(dataProvider = "addressAccessors")
- public void testAddressAccessHyper(String testName, Accessor, ?, ?> accessor) {
- if (testName.contains("index")) {
- accessor.testHyperAligned();
- } else {
- throw new SkipException("Skipping");
- }
- }
-
static final ByteOrder NE = ByteOrder.nativeOrder();
@DataProvider(name = "segmentAccessors")
@@ -187,20 +166,20 @@ static Object[][] segmentAccessors() {
MemorySegment::get, MemorySegment::set,
(bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v))
},
- { "address", Accessor.ofSegment(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
+ { "address", Accessor.ofSegment(ValueLayout.ADDRESS, MemorySegment.ofAddress(42),
MemorySegment::get, MemorySegment::set,
(bb, pos) -> {
ByteBuffer nb = bb.order(NE);
long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
nb.getLong(pos) : nb.getInt(pos);
- return MemoryAddress.ofLong(addr);
+ return MemorySegment.ofAddress(addr);
},
(bb, pos, v) -> {
ByteBuffer nb = bb.order(NE);
if (ValueLayout.ADDRESS.byteSize() == 8) {
- nb.putLong(pos, v.toRawLongValue());
+ nb.putLong(pos, v.address());
} else {
- nb.putInt(pos, (int)v.toRawLongValue());
+ nb.putInt(pos, (int)v.address());
}
})
},
@@ -225,112 +204,23 @@ static Object[][] segmentAccessors() {
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v))
},
- { "address/index", Accessor.ofSegment(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
+ { "address/index", Accessor.ofSegment(ValueLayout.ADDRESS, MemorySegment.ofAddress(42),
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> {
ByteBuffer nb = bb.order(NE);
long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
nb.getLong(pos * 8) : nb.getInt(pos * 4);
- return MemoryAddress.ofLong(addr);
+ return MemorySegment.ofAddress(addr);
},
(bb, pos, v) -> {
ByteBuffer nb = bb.order(NE);
if (ValueLayout.ADDRESS.byteSize() == 8) {
- nb.putLong(pos * 8, v.toRawLongValue());
+ nb.putLong(pos * 8, v.address());
} else {
- nb.putInt(pos * 4, (int)v.toRawLongValue());
+ nb.putInt(pos * 4, (int)v.address());
}
})
},
};
}
-
- @DataProvider(name = "addressAccessors")
- static Object[][] addressAccessors() {
- return new Object[][]{
-
- {"byte", Accessor.ofAddress(ValueLayout.JAVA_BYTE, (byte) 42,
- MemoryAddress::get, MemoryAddress::set,
- ByteBuffer::get, ByteBuffer::put)
- },
- {"bool", Accessor.ofAddress(ValueLayout.JAVA_BOOLEAN, false,
- MemoryAddress::get, MemoryAddress::set,
- (bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0))
- },
- {"char", Accessor.ofAddress(ValueLayout.JAVA_CHAR, (char) 42,
- MemoryAddress::get, MemoryAddress::set,
- (bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v))
- },
- {"int", Accessor.ofAddress(ValueLayout.JAVA_INT, 42,
- MemoryAddress::get, MemoryAddress::set,
- (bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v))
- },
- {"float", Accessor.ofAddress(ValueLayout.JAVA_FLOAT, 42f,
- MemoryAddress::get, MemoryAddress::set,
- (bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v))
- },
- {"long", Accessor.ofAddress(ValueLayout.JAVA_LONG, 42L,
- MemoryAddress::get, MemoryAddress::set,
- (bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v))
- },
- {"double", Accessor.ofAddress(ValueLayout.JAVA_DOUBLE, 42d,
- MemoryAddress::get, MemoryAddress::set,
- (bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v))
- },
- { "address", Accessor.ofAddress(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
- MemoryAddress::get, MemoryAddress::set,
- (bb, pos) -> {
- ByteBuffer nb = bb.order(NE);
- long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
- nb.getLong(pos) : nb.getInt(pos);
- return MemoryAddress.ofLong(addr);
- },
- (bb, pos, v) -> {
- ByteBuffer nb = bb.order(NE);
- if (ValueLayout.ADDRESS.byteSize() == 8) {
- nb.putLong(pos, v.toRawLongValue());
- } else {
- nb.putInt(pos, (int)v.toRawLongValue());
- }
- })
- },
- {"char/index", Accessor.ofAddress(ValueLayout.JAVA_CHAR, (char) 42,
- MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
- (bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v))
- },
- {"int/index", Accessor.ofAddress(ValueLayout.JAVA_INT, 42,
- MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
- (bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v))
- },
- {"float/index", Accessor.ofAddress(ValueLayout.JAVA_FLOAT, 42f,
- MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
- (bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v))
- },
- {"long/index", Accessor.ofAddress(ValueLayout.JAVA_LONG, 42L,
- MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
- (bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v))
- },
- {"double/index", Accessor.ofAddress(ValueLayout.JAVA_DOUBLE, 42d,
- MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
- (bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v))
- },
- { "address/index", Accessor.ofAddress(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
- MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
- (bb, pos) -> {
- ByteBuffer nb = bb.order(NE);
- long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
- nb.getLong(pos * 8) : nb.getInt(pos * 4);
- return MemoryAddress.ofLong(addr);
- },
- (bb, pos, v) -> {
- ByteBuffer nb = bb.order(NE);
- if (ValueLayout.ADDRESS.byteSize() == 8) {
- nb.putLong(pos * 8, v.toRawLongValue());
- } else {
- nb.putInt(pos * 4, (int)v.toRawLongValue());
- }
- })
- }
- };
- }
}
diff --git a/test/jdk/java/foreign/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java
index 225557d18f72e..cc92085422b81 100644
--- a/test/jdk/java/foreign/TestMemoryAlignment.java
+++ b/test/jdk/java/foreign/TestMemoryAlignment.java
@@ -27,11 +27,11 @@
* @run testng TestMemoryAlignment
*/
+import java.lang.foreign.Arena;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemoryLayout.PathElement;
import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.VarHandle;
@@ -52,8 +52,8 @@ public void testAlignedAccess(long align) {
ValueLayout aligned = layout.withBitAlignment(align);
assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws
VarHandle vh = aligned.varHandle();
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(aligned, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(aligned, arena.scope());;
vh.set(segment, -42);
int val = (int)vh.get(segment);
assertEquals(val, -42);
@@ -70,8 +70,8 @@ public void testUnalignedAccess(long align) {
MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned);
assertEquals(alignedGroup.bitAlignment(), align);
VarHandle vh = aligned.varHandle();
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(alignedGroup, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(alignedGroup, arena.scope());;
vh.set(segment.asSlice(1L), -42);
assertEquals(align, 8); //this is the only case where access is aligned
} catch (IllegalArgumentException ex) {
@@ -97,8 +97,8 @@ public void testUnalignedSequence(long align) {
SequenceLayout layout = MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withBitAlignment(align));
try {
VarHandle vh = layout.varHandle(PathElement.sequenceElement());
- try (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(layout, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());;
for (long i = 0 ; i < 5 ; i++) {
vh.set(segment, i, -42);
}
@@ -121,8 +121,8 @@ public void testPackedAccess() {
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 (MemorySession session = MemorySession.openConfined()) {
- MemorySegment segment = MemorySegment.allocateNative(g, session);
+ try (Arena arena = Arena.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(g, arena.scope());;
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/TestMemoryDereference.java b/test/jdk/java/foreign/TestMemoryDereference.java
index d6d5af5f94909..13dd26ce749d8 100644
--- a/test/jdk/java/foreign/TestMemoryDereference.java
+++ b/test/jdk/java/foreign/TestMemoryDereference.java
@@ -27,7 +27,6 @@
* @run testng TestMemoryDereference
*/
-import java.lang.foreign.MemoryAddress;
import java.lang.foreign.MemorySegment;
import java.nio.ByteBuffer;
@@ -36,14 +35,7 @@
import java.lang.foreign.ValueLayout;
import org.testng.annotations.*;
-import static java.lang.foreign.ValueLayout.ADDRESS;
-import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN;
-import static java.lang.foreign.ValueLayout.JAVA_BYTE;
-import static java.lang.foreign.ValueLayout.JAVA_CHAR;
-import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
-import static java.lang.foreign.ValueLayout.JAVA_FLOAT;
-import static java.lang.foreign.ValueLayout.JAVA_INT;
-import static java.lang.foreign.ValueLayout.JAVA_SHORT;
+import static java.lang.foreign.ValueLayout.*;
import static org.testng.Assert.*;
public class TestMemoryDereference {
@@ -98,14 +90,6 @@ Accessor of(Z value,
}
}
- // unaligned constants
- public final static ValueLayout.OfShort JAVA_SHORT_UNALIGNED = JAVA_SHORT.withBitAlignment(8);
- public final static ValueLayout.OfChar JAVA_CHAR_UNALIGNED = JAVA_CHAR.withBitAlignment(8);
- public final static ValueLayout.OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8);
- public final static ValueLayout.OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8);
- public final static ValueLayout.OfDouble JAVA_DOUBLE_UNALIGNED = JAVA_DOUBLE.withBitAlignment(8);
- public final static ValueLayout.OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8);
-
@Test(dataProvider = "accessors")
public void testMemoryAccess(String testName, Accessor> accessor) {
accessor.test();
@@ -204,20 +188,20 @@ static Object[][] accessors() {
(s, x) -> s.set(JAVA_DOUBLE_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN), 8, x),
(bb) -> bb.order(BE).getDouble(8), (bb, v) -> bb.order(BE).putDouble(8, v))
},
- { "address/offset", new Accessor<>(MemoryAddress.ofLong(42),
+ { "address/offset", new Accessor<>(MemorySegment.ofAddress(42),
s -> s.get(ADDRESS_UNALIGNED, 8), (s, x) -> s.set(ADDRESS_UNALIGNED, 8, x),
(bb) -> {
ByteBuffer nb = bb.order(NE);
long addr = ADDRESS_UNALIGNED.byteSize() == 8 ?
nb.getLong(8) : nb.getInt(8);
- return MemoryAddress.ofLong(addr);
+ return MemorySegment.ofAddress(addr);
},
(bb, v) -> {
ByteBuffer nb = bb.order(NE);
if (ADDRESS_UNALIGNED.byteSize() == 8) {
- nb.putLong(8, v.toRawLongValue());
+ nb.putLong(8, v.address());
} else {
- nb.putInt(8, (int)v.toRawLongValue());
+ nb.putInt(8, (int)v.address());
}
})
},
diff --git a/test/jdk/java/foreign/TestMemorySession.java b/test/jdk/java/foreign/TestMemorySession.java
index ff206255cc42b..6501978f725ed 100644
--- a/test/jdk/java/foreign/TestMemorySession.java
+++ b/test/jdk/java/foreign/TestMemorySession.java
@@ -28,13 +28,11 @@
* @run testng/othervm TestMemorySession
*/
-import java.lang.ref.Cleaner;
+import java.lang.foreign.Arena;
-import java.lang.foreign.MemorySession;
+import java.lang.foreign.SegmentScope;
import jdk.internal.foreign.MemorySessionImpl;
-import jdk.internal.ref.CleanerFactory;
-
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@@ -43,57 +41,39 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Function;
import java.util.function.Supplier;
-import java.util.function.UnaryOperator;
import java.util.stream.IntStream;
public class TestMemorySession {
final static int N_THREADS = 100;
- @Test(dataProvider = "cleaners")
- public void testConfined(Supplier cleanerSupplier, UnaryOperator sessionFunc) {
+ @Test
+ public void testConfined() {
AtomicInteger acc = new AtomicInteger();
- Cleaner cleaner = cleanerSupplier.get();
- MemorySession session = cleaner != null ?
- MemorySession.openConfined(cleaner) :
- MemorySession.openConfined();
- session = sessionFunc.apply(session);
+ Arena arena = Arena.openConfined();
for (int i = 0 ; i < N_THREADS ; i++) {
int delta = i;
- session.addCloseAction(() -> acc.addAndGet(delta));
+ addCloseAction(arena.scope(), () -> acc.addAndGet(delta));
}
assertEquals(acc.get(), 0);
- if (cleaner == null) {
- session.close();
- assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum());
- } else {
- session = null;
- int expected = IntStream.range(0, N_THREADS).sum();
- while (acc.get() != expected) {
- kickGC();
- }
- }
+ arena.close();
+ assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum());
}
- @Test(dataProvider = "cleaners")
- public void testSharedSingleThread(Supplier cleanerSupplier, UnaryOperator sessionFunc) {
+ @Test(dataProvider = "sharedSessions")
+ public void testSharedSingleThread(SessionSupplier sessionSupplier) {
AtomicInteger acc = new AtomicInteger();
- Cleaner cleaner = cleanerSupplier.get();
- MemorySession session = cleaner != null ?
- MemorySession.openShared(cleaner) :
- MemorySession.openShared();
- session = sessionFunc.apply(session);
+ SegmentScope session = sessionSupplier.get();
for (int i = 0 ; i < N_THREADS ; i++) {
int delta = i;
- session.addCloseAction(() -> acc.addAndGet(delta));
+ addCloseAction(session, () -> acc.addAndGet(delta));
}
assertEquals(acc.get(), 0);
- if (cleaner == null) {
- session.close();
+ if (!SessionSupplier.isImplicit(session)) {
+ SessionSupplier.close(session);
assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum());
} else {
session = null;
@@ -104,21 +84,17 @@ public void testSharedSingleThread(Supplier cleanerSupplier, UnaryOpera
}
}
- @Test(dataProvider = "cleaners")
- public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperator sessionFunc) {
+ @Test(dataProvider = "sharedSessions")
+ public void testSharedMultiThread(SessionSupplier sessionSupplier) {
AtomicInteger acc = new AtomicInteger();
- Cleaner cleaner = cleanerSupplier.get();
List threads = new ArrayList<>();
- MemorySession session = cleaner != null ?
- MemorySession.openShared(cleaner) :
- MemorySession.openShared();
- session = sessionFunc.apply(session);
- AtomicReference sessionRef = new AtomicReference<>(session);
+ SegmentScope session = sessionSupplier.get();
+ AtomicReference sessionRef = new AtomicReference<>(session);
for (int i = 0 ; i < N_THREADS ; i++) {
int delta = i;
Thread thread = new Thread(() -> {
try {
- sessionRef.get().addCloseAction(() -> {
+ addCloseAction(sessionRef.get(), () -> {
acc.addAndGet(delta);
});
} catch (IllegalStateException ex) {
@@ -133,10 +109,10 @@ public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperat
// 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 (cleaner == null) {
+ if (!SessionSupplier.isImplicit(session)) {
while (true) {
try {
- session.close();
+ SessionSupplier.close(session);
break;
} catch (IllegalStateException ise) {
// session is acquired (by add) - wait some more
@@ -152,7 +128,7 @@ public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperat
}
});
- if (cleaner == null) {
+ if (!SessionSupplier.isImplicit(session)) {
assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum());
} else {
session = null;
@@ -166,22 +142,22 @@ public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperat
@Test
public void testLockSingleThread() {
- MemorySession session = MemorySession.openConfined();
- List handles = new ArrayList<>();
+ Arena arena = Arena.openConfined();
+ List handles = new ArrayList<>();
for (int i = 0 ; i < N_THREADS ; i++) {
- MemorySession handle = MemorySession.openConfined();
- keepAlive(handle, session);
+ Arena handle = Arena.openConfined();
+ keepAlive(handle.scope(), arena.scope());
handles.add(handle);
}
while (true) {
try {
- session.close();
+ arena.close();
assertEquals(handles.size(), 0);
break;
} catch (IllegalStateException ex) {
assertTrue(handles.size() > 0);
- MemorySession handle = handles.remove(0);
+ Arena handle = handles.remove(0);
handle.close();
}
}
@@ -189,16 +165,15 @@ public void testLockSingleThread() {
@Test
public void testLockSharedMultiThread() {
- MemorySession session = MemorySession.openShared();
+ Arena arena = Arena.openShared();
AtomicInteger lockCount = new AtomicInteger();
for (int i = 0 ; i < N_THREADS ; i++) {
new Thread(() -> {
- try (MemorySession handle = MemorySession.openConfined()) {
- keepAlive(handle, session);
+ try (Arena handle = Arena.openConfined()) {
+ keepAlive(handle.scope(), arena.scope());
lockCount.incrementAndGet();
waitSomeTime();
lockCount.decrementAndGet();
- handle.close();
} catch (IllegalStateException ex) {
// might be already closed - do nothing
}
@@ -207,7 +182,7 @@ public void testLockSharedMultiThread() {
while (true) {
try {
- session.close();
+ arena.close();
assertEquals(lockCount.get(), 0);
break;
} catch (IllegalStateException ex) {
@@ -218,19 +193,19 @@ public void testLockSharedMultiThread() {
@Test
public void testCloseEmptyConfinedSession() {
- MemorySession.openConfined().close();
+ Arena.openConfined().close();
}
@Test
public void testCloseEmptySharedSession() {
- MemorySession.openShared().close();
+ Arena.openShared().close();
}
@Test
public void testCloseConfinedLock() {
- MemorySession session = MemorySession.openConfined();
- MemorySession handle = MemorySession.openConfined();
- keepAlive(handle, session);
+ Arena arena = Arena.openConfined();
+ Arena handle = Arena.openConfined();
+ keepAlive(handle.scope(), arena.scope());
AtomicReference failure = new AtomicReference<>();
Thread t = new Thread(() -> {
try {
@@ -249,34 +224,33 @@ public void testCloseConfinedLock() {
}
}
- @Test(dataProvider = "sessions")
- public void testSessionAcquires(Supplier sessionFactory) {
- MemorySession session = sessionFactory.get();
+ @Test(dataProvider = "allSessions")
+ public void testSessionAcquires(SessionSupplier sessionSupplier) {
+ SegmentScope session = sessionSupplier.get();
acquireRecursive(session, 5);
- if (session.isCloseable()) {
- session.close();
- }
+ if (!SessionSupplier.isImplicit(session))
+ SessionSupplier.close(session);
}
- private void acquireRecursive(MemorySession session, int acquireCount) {
- try (MemorySession handle = MemorySession.openConfined()) {
- keepAlive(handle, session);
+ private void acquireRecursive(SegmentScope session, int acquireCount) {
+ try (Arena arena = Arena.openConfined()) {
+ keepAlive(arena.scope(), session);
if (acquireCount > 0) {
// recursive acquire
acquireRecursive(session, acquireCount - 1);
}
- if (session.isCloseable()) {
- assertThrows(IllegalStateException.class, session::close);
+ if (!SessionSupplier.isImplicit(session)) {
+ assertThrows(IllegalStateException.class, () -> SessionSupplier.close(session));
}
}
}
@Test
public void testConfinedSessionWithImplicitDependency() {
- MemorySession root = MemorySession.openConfined();
+ Arena root = Arena.openConfined();
// Create many implicit sessions which depend on 'root', and let them become unreachable.
for (int i = 0; i < N_THREADS; i++) {
- keepAlive(MemorySession.openConfined(Cleaner.create()), root);
+ keepAlive(SegmentScope.auto(), root.scope());
}
// 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.
@@ -288,8 +262,8 @@ public void testConfinedSessionWithImplicitDependency() {
} catch (IllegalStateException ex) {
kickGC();
for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread
- try (MemorySession session = MemorySession.openConfined()) {
- keepAlive(session, root);
+ try (Arena arena = Arena.openConfined()) {
+ keepAlive(arena.scope(), root.scope());
// dummy
}
}
@@ -300,19 +274,19 @@ public void testConfinedSessionWithImplicitDependency() {
@Test
public void testConfinedSessionWithSharedDependency() {
- MemorySession root = MemorySession.openConfined();
+ Arena root = Arena.openConfined();
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++) {
- MemorySession session = MemorySession.openShared(); // create session inside same thread!
- keepAlive(session, root);
- Thread t = new Thread(session::close); // close from another thread!
+ Arena arena = Arena.openShared(); // create session inside same thread!
+ keepAlive(arena.scope(), root.scope());
+ 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 (MemorySession session = MemorySession.openConfined()) {
- keepAlive(session, root);
+ try (Arena arena = Arena.openConfined()) {
+ keepAlive(arena.scope(), root.scope());
// dummy
}
}
@@ -345,33 +319,57 @@ private void kickGC() {
}
@DataProvider
- static Object[][] cleaners() {
+ static Object[][] drops() {
return new Object[][] {
- { (Supplier)() -> null, UnaryOperator.identity() },
- { (Supplier)Cleaner::create, UnaryOperator.identity() },
- { (Supplier)CleanerFactory::cleaner, UnaryOperator.identity() },
- { (Supplier)Cleaner::create, (UnaryOperator)MemorySession::asNonCloseable },
- { (Supplier)CleanerFactory::cleaner, (UnaryOperator)MemorySession::asNonCloseable }
+ { (Supplier) Arena::openConfined},
+ { (Supplier) Arena::openShared},
};
}
- @DataProvider
- static Object[][] sessions() {
+ private void keepAlive(SegmentScope child, SegmentScope parent) {
+ MemorySessionImpl parentImpl = (MemorySessionImpl) parent;
+ parentImpl.acquire0();
+ addCloseAction(child, parentImpl::release0);
+ }
+
+ private void addCloseAction(SegmentScope session, Runnable action) {
+ MemorySessionImpl sessionImpl = (MemorySessionImpl) session;
+ sessionImpl.addCloseAction(action);
+ }
+
+ interface SessionSupplier extends Supplier {
+
+ static void close(SegmentScope session) {
+ ((MemorySessionImpl)session).close();
+ }
+
+ static boolean isImplicit(SegmentScope session) {
+ return !((MemorySessionImpl)session).isCloseable();
+ }
+
+ static SessionSupplier ofImplicit() {
+ return SegmentScope::auto;
+ }
+
+ static SessionSupplier ofArena(Supplier arenaSupplier) {
+ return () -> arenaSupplier.get().scope();
+ }
+ }
+
+ @DataProvider(name = "sharedSessions")
+ static Object[][] sharedSessions() {
return new Object[][] {
- { (Supplier) MemorySession::openConfined},
- { (Supplier) MemorySession::openShared},
- { (Supplier) MemorySession::openImplicit},
- { (Supplier) MemorySession::global},
- { (Supplier) () -> MemorySession.openConfined().asNonCloseable() },
- { (Supplier) () -> MemorySession.openShared().asNonCloseable() },
- { (Supplier