diff --git a/src/java.base/share/classes/java/lang/foreign/AddressLayout.java b/src/java.base/share/classes/java/lang/foreign/AddressLayout.java index 9819cc84cc9..877a6432961 100644 --- a/src/java.base/share/classes/java/lang/foreign/AddressLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/AddressLayout.java @@ -37,7 +37,7 @@ import java.util.Optional; /** * A value layout used to model the address of some region of memory. The carrier associated with an address layout is - * {@code MemorySegment.class}. The size and alignment of an address layout are platform dependent + * {@code MemorySegment.class}. The size and alignment of an address layout are platform-dependent * (e.g. on a 64-bit platform, the size and alignment of an address layout are set to 8 bytes). *

* An address layout may optionally feature a {@linkplain #targetLayout() target layout}. An address layout with @@ -113,9 +113,9 @@ public sealed interface AddressLayout extends ValueLayout permits ValueLayouts.O /** * Returns an address layout with the same carrier, alignment constraint, name and order as this address layout, - * but without any specified target layout. - *

- * This can be useful to compare two address layouts that have different target layouts, but are otherwise equal. + * but with no target layout. + * + * @apiNote This can be useful to compare two address layouts that have different target layouts, but are otherwise equal. * * @return an address layout with same characteristics as this layout, but with no target layout. * @see #targetLayout() diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java index f111368d7d5..1f3c1adc16d 100644 --- a/src/java.base/share/classes/java/lang/foreign/Arena.java +++ b/src/java.base/share/classes/java/lang/foreign/Arena.java @@ -56,7 +56,7 @@ import java.lang.foreign.MemorySegment.Scope; * Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena * which features a bounded lifetime that is managed, automatically, by the garbage collector. As such, the regions * of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time - * after the automatic arena (and all the segments allocated by it) become + * after the automatic arena (and all the segments allocated by it) becomes * unreachable, as shown below: * {@snippet lang = java: * MemorySegment segment = Arena.ofAuto().allocate(100, 1); // @highlight regex='ofAuto()' @@ -202,7 +202,7 @@ public interface Arena extends SegmentAllocator, AutoCloseable { /** * Creates a new arena that is managed, automatically, by the garbage collector. - * Segments obtained with the returned arena can be + * Segments allocated with the returned arena can be * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. * Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}. * @@ -213,7 +213,7 @@ public interface Arena extends SegmentAllocator, AutoCloseable { } /** - * Obtains the global arena. Segments obtained with the global arena can be + * Obtains the global arena. Segments allocated with the global arena can be * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. * Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}. * @@ -227,14 +227,17 @@ public interface Arena extends SegmentAllocator, AutoCloseable { } /** - * {@return a new confined arena, owned by the current thread} + * {@return a new confined arena} Segments allocated with the confined arena can be + * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by the thread that created the arena, + * the arena's owner thread. */ static Arena ofConfined() { return MemorySessionImpl.createConfined(Thread.currentThread()).asArena(); } /** - * {@return a new shared arena} + * {@return a new shared arena} Segments allocated with the global arena can be + * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. */ static Arena ofShared() { return MemorySessionImpl.createShared().asArena(); @@ -244,7 +247,7 @@ 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 this {@linkplain #scope() arena scope}. * The segment's {@link MemorySegment#address() address} is the starting address of the - * allocated off-heap memory region backing the segment, and the address is + * allocated off-heap region of memory backing the segment, and the address is * aligned according the provided alignment constraint. * * @implSpec @@ -256,14 +259,14 @@ public interface Arena extends SegmentAllocator, AutoCloseable { * S1.asOverlappingSlice(S2).isEmpty() == true * } * - * @param byteSize the size (in bytes) of the off-heap memory block backing the native memory segment. + * @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. * @return a new native memory segment. - * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes} + * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code byteAlignment <= 0}, or if {@code byteAlignment} * is not a power of 2. * @throws IllegalStateException if this arena has already been {@linkplain #close() closed}. - * @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T} - * other than the arena owner thread. + * @throws WrongThreadException if this arena is confined, and this method is called from a thread + * other than the arena's owner thread. */ @Override default MemorySegment allocate(long byteSize, long byteAlignment) { @@ -293,9 +296,9 @@ public interface Arena extends SegmentAllocator, AutoCloseable { * @throws IllegalStateException if the arena has already been closed. * @throws IllegalStateException if a segment associated with this arena is being accessed concurrently, e.g. * by a {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}. - * @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T} - * other than the arena owner thread. - * @throws UnsupportedOperationException if this arena does not support explicit closure. + * @throws WrongThreadException if this arena is confined, and this method is called from a thread + * other than the arena's owner thread. + * @throws UnsupportedOperationException if this arena cannot be closed explicitly. */ @Override void close(); 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 ab2fa82b10e..f536f6fdcf9 100644 --- a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java +++ b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java @@ -34,9 +34,9 @@ import jdk.internal.foreign.FunctionDescriptorImpl; import jdk.internal.javac.PreviewFeature; /** - * A function descriptor models the signature of foreign functions. A function descriptor is made up of zero or more - * argument layouts and zero or one return layout. A function descriptor is typically used when creating - * {@linkplain Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) downcall method handles} or + * A function descriptor models the signature of a foreign function. A function descriptor is made up of zero or more + * argument layouts, and zero or one return layout. A function descriptor is used to create + * {@linkplain Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) downcall method handles} and * {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Linker.Option...) upcall stubs}. * * @implSpec @@ -49,21 +49,21 @@ import jdk.internal.javac.PreviewFeature; public sealed interface FunctionDescriptor permits FunctionDescriptorImpl { /** - * {@return the return layout (if any) associated with this function descriptor} + * {@return the return layout (if any) of this function descriptor} */ Optional returnLayout(); /** - * {@return the argument layouts associated with this function descriptor (as an immutable list)}. + * {@return the argument layouts of this function descriptor (as an unmodifiable list)}. */ List argumentLayouts(); /** - * Returns a function descriptor with the given argument layouts appended to the argument layout array + * Returns a function descriptor with the given argument layouts appended to the argument layouts * of this function descriptor. * @param addedLayouts the argument layouts to append. * @throws IllegalArgumentException if one of the layouts in {@code addedLayouts} is a padding layout. - * @return the new function descriptor. + * @return a new function descriptor, with the provided additional argument layouts. */ FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts); @@ -72,42 +72,38 @@ public sealed interface FunctionDescriptor permits FunctionDescriptorImpl { * 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. + * @return a new function descriptor, with the provided additional argument layouts. * @throws IllegalArgumentException if one of the layouts in {@code addedLayouts} is a padding layout. * @throws IllegalArgumentException if {@code index < 0 || index > argumentLayouts().size()}. */ FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts); /** - * Returns a function descriptor with the given memory layout as the new return layout. + * Returns a function descriptor with the provided return layout. * @param newReturn the new return layout. * @throws IllegalArgumentException if {@code newReturn} is a padding layout. - * @return the new function descriptor. + * @return a new function descriptor, with the provided return layout. */ 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. + * {@return a new function descriptor, with no return layout} */ FunctionDescriptor dropReturnLayout(); /** * 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 carrier type of a layout {@code L} is determined as follows: *

* * @apiNote A function descriptor cannot, by construction, contain any padding layouts. As such, it is not * necessary to specify how padding layout should be mapped to carrier types. * - * @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). + * @return the method type consisting of the carrier types of the layouts in this function descriptor. */ MethodType toMethodType(); @@ -117,7 +113,7 @@ public sealed interface FunctionDescriptor permits FunctionDescriptorImpl { * @param argLayouts the argument layouts. * @throws IllegalArgumentException if {@code resLayout} is a padding layout. * @throws IllegalArgumentException if one of the layouts in {@code argLayouts} is a padding layout. - * @return the new function descriptor. + * @return a new function descriptor with the provided return and argument layouts. */ static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) { Objects.requireNonNull(resLayout); @@ -126,10 +122,11 @@ public sealed interface FunctionDescriptor permits FunctionDescriptorImpl { } /** - * Creates a function descriptor with the given argument layouts and no return layout. + * Creates a function descriptor with the given argument layouts and no return layout. This is useful to model functions + * that return no values. * @param argLayouts the argument layouts. * @throws IllegalArgumentException if one of the layouts in {@code argLayouts} is a padding layout. - * @return the new function descriptor. + * @return a new function descriptor with the provided argument layouts. */ static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) { // Null checks are implicit in 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 20f6f29fd30..895678f6fd3 100644 --- a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java @@ -29,10 +29,10 @@ import java.util.List; 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 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 layout (see {@link MemoryLayout#unionLayout(MemoryLayout...)}). + * A compound layout that is an aggregation of multiple, heterogeneous 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 a + * {@linkplain StructLayout struct layout}; conversely, if all member layouts are laid out at the same starting offset, + * the resulting group layout is a {@linkplain UnionLayout union layout}. * * @implSpec * This class is immutable, thread-safe and value-based. @@ -43,13 +43,11 @@ import jdk.internal.javac.PreviewFeature; public sealed interface GroupLayout extends MemoryLayout permits StructLayout, UnionLayout { /** - * Returns the member layouts associated with this group. + * {@return the member layouts of this group layout} * * @apiNote the order in which member layouts are returned is the same order in which member layouts have * been passed to one of the group layout factory methods (see {@link MemoryLayout#structLayout(MemoryLayout...)}, * {@link MemoryLayout#unionLayout(MemoryLayout...)}). - * - * @return the member layouts associated with this group. */ List memberLayouts(); 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 9862ded081d..f374755e193 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -197,16 +197,41 @@ import java.util.stream.Stream; * * *

- * All the native linker implementations limit the function descriptors that they support to those that contain - * only so-called canonical layouts. A canonical layout has the following characteristics: + * All native linker implementations operate on a subset of memory layouts. More formally, a layout {@code L} + * is supported by a native linker {@code NL} if: + *

+ * + * A native linker only supports function descriptors whose argument/return layouts are layouts supported by that linker + * and are not sequence layouts. * *

Function pointers

* @@ -317,8 +342,8 @@ import java.util.stream.Stream; * ); * } * - * When interacting with a native functions returning a pointer (such as {@code malloc}), the Java runtime has no insight - * into the size or the lifetime of the returned pointer. Consider the following code: + * When a native function returning a pointer (such as {@code malloc}) is invoked using a downcall method handle, + * the Java runtime has no insight into the size or the lifetime of the returned pointer. Consider the following code: * * {@snippet lang = java: * MemorySegment segment = (MemorySegment)malloc.invokeExact(100); @@ -330,8 +355,8 @@ import java.util.stream.Stream; * unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to * attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory * backing the segment can be managed automatically, as for any other native segment created directly from Java code. - * Both these operations are accomplished using the restricted {@link MemorySegment#reinterpret(long, Arena, Consumer)} - * method, as follows: + * Both of these operations are accomplished using the restricted method {@link MemorySegment#reinterpret(long, Arena, Consumer)}, + * as follows: * * {@snippet lang = java: * MemorySegment allocateMemory(long byteSize, Arena arena) throws Throwable { @@ -415,12 +440,12 @@ import java.util.stream.Stream; * } * * To perform an equivalent call using a downcall method handle we must create a function descriptor which - * describes the specialized signature of the C function we want to call. This descriptor must include layouts for any - * additional variadic argument we intend to provide. In this case, the specialized signature of the C - * function is {@code (char*, int, int, int)} as the format string accepts three integer parameters. Then, we need to use - * a linker option to specify the position of the first variadic layout in the provided function descriptor (starting from 0). - * In this case, since the first parameter is the format string (a non-variadic argument), the first variadic index - * needs to be set to 1, as follows: + * describes the specialized signature of the C function we want to call. This descriptor must include an additional layout + * for each variadic argument we intend to provide. In this case, the specialized signature of the C + * function is {@code (char*, int, int, int)} as the format string accepts three integer parameters. We then need to use + * a {@linkplain Linker.Option#firstVariadicArg(int) linker option} to specify the position of the first variadic layout + * in the provided function descriptor (starting from 0). In this case, since the first parameter is the format string + * (a non-variadic argument), the first variadic index needs to be set to 1, as follows: * * {@snippet lang = java: * Linker linker = Linker.nativeLinker(); @@ -447,10 +472,9 @@ import java.util.stream.Stream; * 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. *

- * 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 + * When an upcall stub is passed to a foreign function, a JVM crash might occur, if the foreign code casts the function pointer + * associated with the upcall stub to a type that is incompatible with the type of the upcall stub, and then attempts to + * invoke the function through the resulting function pointer. Moreover, if the method * 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. @@ -464,7 +488,7 @@ import java.util.stream.Stream; public sealed interface Linker permits AbstractLinker { /** - * Returns a linker for the ABI associated with the underlying native platform. The underlying native platform + * {@return a linker for the ABI associated with the underlying native platform} The underlying native platform * is the combination of OS and processor where the Java runtime is currently executing. * * @apiNote It is not currently possible to obtain a linker for a different combination of OS and processor. @@ -472,7 +496,6 @@ public sealed interface Linker permits AbstractLinker { * linker are the native libraries loaded in the process where the Java runtime is currently executing. For example, * on Linux, these libraries typically include {@code libc}, {@code libm} and {@code libdl}. * - * @return a linker for the ABI associated with the underlying native platform. * @throws UnsupportedOperationException if the underlying native platform is not supported. */ static Linker nativeLinker() { @@ -492,17 +515,20 @@ public sealed interface Linker permits AbstractLinker { * 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 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 + * @param address the native memory segment whose {@linkplain MemorySegment#address() base address} is the + * address of the target foreign function. + * @param function the function descriptor of the target foreign function. + * @param options the linker options associated with this linkage request. + * @return a downcall method handle. * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. - * or if the symbol is {@link MemorySegment#NULL} + * @throws IllegalArgumentException if {@code !address.isNative()}, or if {@code address.equals(MemorySegment.NULL)}. * @throws IllegalArgumentException if an invalid combination of linker options is given. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. + * + * @see SymbolLookup */ @CallerSensitive - MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options); + MethodHandle downcallHandle(MemorySegment address, FunctionDescriptor function, Option... options); /** * Creates a method handle which is used to call a foreign function with the given signature. @@ -514,22 +540,22 @@ public sealed interface Linker permits AbstractLinker { * downcall method handle accepts an additional leading parameter of type {@link SegmentAllocator}, which is used by * the linker runtime to allocate the memory region associated with the struct returned by the downcall method handle. *

- * Upon invoking a downcall method handle, the linker runtime will guarantee the following for any argument + * Upon invoking a downcall method handle, the linker provides the following guarantees for any argument * {@code A} of type {@link MemorySegment} whose corresponding layout is an {@linkplain AddressLayout address layout}: *

*

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

* The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} * representing the target address of the foreign function is the {@link MemorySegment#NULL} address. @@ -540,10 +566,9 @@ public sealed interface Linker permits AbstractLinker { * 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 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. + * @param function the function descriptor of the target foreign function. + * @param options the linker options associated with this linkage request. + * @return a downcall method handle. * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. * @throws IllegalArgumentException if an invalid combination of linker options is given. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. @@ -552,7 +577,7 @@ public sealed interface Linker permits AbstractLinker { MethodHandle downcallHandle(FunctionDescriptor function, Option... options); /** - * Creates a stub which can be passed to other foreign functions as a function pointer, associated with the given + * Creates an upcall stub which can be passed to other foreign functions as a function pointer, associated with the given * arena. Calling such a function pointer from foreign code will result in the execution of the provided * method handle. *

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

* The target method handle should not throw any exceptions. If the target method handle does throw an exception, - * the VM will exit with a non-zero exit code. To avoid the VM aborting due to an uncaught exception, clients - * could wrap all code in the target method handle in a try/catch block that catches any {@link Throwable}, for - * instance by using the {@link java.lang.invoke.MethodHandles#catchException(MethodHandle, Class, MethodHandle)} - * method handle combinator, and handle exceptions as desired in the corresponding catch block. + * the JVM will terminate abruptly. To avoid this, clients should wrap the code in the target method handle in a + * try/catch block to catch any unexpected exceptions. This can be done using the + * {@link java.lang.invoke.MethodHandles#catchException(MethodHandle, Class, MethodHandle)} method handle combinator, + * and handle exceptions as desired in the corresponding catch block. *

* This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash @@ -581,11 +606,12 @@ public sealed interface Linker permits AbstractLinker { * @param target the target method handle. * @param function the upcall stub function descriptor. * @param arena the arena associated with the returned upcall stub segment. - * @param options any linker options. + * @param options the linker options associated with this linkage request. * @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 IllegalArgumentException if the type of {@code target} is incompatible with the + * type {@linkplain FunctionDescriptor#toMethodType() derived} from {@code function}. + * @throws IllegalArgumentException if it is determined that the target method handle can throw an exception. * @throws IllegalStateException if {@code arena.scope().isAlive() == false} * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a * thread {@code T}, other than the arena's owner thread. @@ -610,8 +636,7 @@ public sealed interface Linker permits AbstractLinker { SymbolLookup defaultLookup(); /** - * A linker option is used to indicate additional linking requirements to the linker, - * besides what is described by a function descriptor. + * A linker option is used to provide additional parameters to a linkage request. * @since 20 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) @@ -619,9 +644,10 @@ public sealed interface Linker permits AbstractLinker { permits LinkerOptions.LinkerOptionImpl { /** - * {@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. + * {@return a linker option used to denote the index of the first variadic argument layout in the + * function descriptor associated with a downcall linkage request} + * @param index the index of the first variadic argument layout in the function descriptor associated + * with a downcall linkage request. */ static Option firstVariadicArg(int index) { return new LinkerOptions.FirstVariadicArg(index); @@ -637,11 +663,12 @@ public sealed interface Linker permits AbstractLinker { * For this purpose, a downcall method handle linked with this * option will feature an additional {@link MemorySegment} parameter directly * following the target address, and optional {@link SegmentAllocator} parameters. - * This parameter, called the 'capture state segment', represents the native segment into which + * This parameter, the capture state segment, represents the native segment into which * the captured state is written. *

- * The capture state segment should have the layout returned by {@linkplain #captureStateLayout}. - * This layout is a struct layout which has a named field for each captured value. + * The capture state segment must have size and alignment compatible with the layout returned by + * {@linkplain #captureStateLayout}. This layout is a struct layout which has a named field for + * each captured value. *

* Captured state can be retrieved from the capture state segment by constructing var handles * from the {@linkplain #captureStateLayout capture state layout}. @@ -677,9 +704,9 @@ public sealed interface Linker permits AbstractLinker { /** * {@return a struct layout that represents the layout of the capture state segment that is passed - * to a downcall handle linked with {@link #captureCallState(String...)}}. + * to a downcall handle linked with {@link #captureCallState(String...)}} *

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

  • WSAGetLastError
  • *
  • errno
  • * - * The following snipet shows how to obtain the names of the supported captured value layouts: + *

    + * Clients can obtain the names of the supported captured value layouts as follows: * {@snippet lang = java: - * String capturedNames = Linker.Option.captureStateLayout().memberLayouts().stream() + * List capturedNames = Linker.Option.captureStateLayout().memberLayouts().stream() * .map(MemoryLayout::name) * .flatMap(Optional::stream) - * .map(Objects::toString) - * .collect(Collectors.joining(", ")); + * .toList(); * } * * @see #captureCallState(String...) 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 78f3e6758f6..10f57aa436a 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -33,7 +33,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Stream; import jdk.internal.foreign.LayoutPath; @@ -48,14 +47,19 @@ import jdk.internal.javac.PreviewFeature; /** * 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)}). - * Some common value layout constants are defined in the {@link ValueLayout} class. *

    - * More complex layouts can be derived from simpler ones: a sequence layout denotes a repetition of one or more - * element layout (see {@link SequenceLayout}); a group layout denotes an aggregation of (typically) heterogeneous - * member layouts (see {@link GroupLayout}). + * There are two leaves in the layout hierarchy, {@linkplain ValueLayout value layouts}, which are used to represent values of given size and kind (see + * and {@linkplain PaddingLayout 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. + * Some common value layout constants, such as {@link ValueLayout#JAVA_INT} and {@link ValueLayout#JAVA_FLOAT_UNALIGNED} + * are defined in the {@link ValueLayout} class. A special kind of value layout, namely an {@linkplain AddressLayout address layout}, + * is used to model values that denote the address of a region of memory. + *

    + * More complex layouts can be derived from simpler ones: a {@linkplain SequenceLayout sequence layout} denotes a + * homogeneous repetition of zero or more occurrences of an element layout; a {@linkplain GroupLayout group layout} + * denotes a heterogeneous aggregation of zero or more member layouts. Group layouts come in two + * flavors: {@linkplain StructLayout struct layouts}, where member layouts are laid out one after the other, and + * {@linkplain UnionLayout union layouts} where member layouts are laid out at the same starting offset. *

    * Layouts can be optionally associated with a name. A layout name can be referred to when * constructing layout paths. @@ -81,47 +85,50 @@ import jdk.internal.javac.PreviewFeature; * ).withName("TaggedValues"); * } * - *

    Size, alignment and byte order

    + *

    Characteristics of memory layouts

    * - * All layouts have a size; layout size for value and padding layouts is always explicitly denoted; this means that a layout description - * always has the same size in bytes, regardless of the platform in which it is used. For derived layouts, the size is computed - * as follows: + * All layouts have a size (expressed in bytes), which is defined as follows: * *

    - * Furthermore, all layouts feature a natural alignment which can be inferred as follows: + * Furthermore, all layouts have a natural alignment (expressed in bytes) which is defined as follows: *

    - * A layout's natural alignment can be overridden if needed (see {@link MemoryLayout#withByteAlignment(long)}), which can be useful to describe - * hyper-aligned layouts. - *

    - * All value layouts have an explicit byte order (see {@link java.nio.ByteOrder}) which is set when the layout is created. + * A layout's alignment can be overridden if needed (see {@link MemoryLayout#withByteAlignment(long)}), which can be useful to describe + * layouts with weaker or stronger alignment constraints. * *

    Layout paths

    * - * A layout path originates from a root layout (typically a group or a sequence layout) and terminates - * at a layout nested within the root layout - this is the layout selected by the layout path. - * Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances. + * A layout path is used to unambiguously select a layout that is nested in some other layout. + * Layout paths are typically expressed as a sequence of one or more {@linkplain PathElement path elements}. + * (A more formal definition of layout paths is provided below). *

    - * Layout paths are for example useful in order to obtain {@linkplain MemoryLayout#byteOffset(PathElement...) offsets} of - * arbitrarily nested layouts inside another layout, to quickly obtain a {@linkplain #varHandle(PathElement...) memory access handle} - * corresponding to the selected layout, or to {@linkplain #select(PathElement...) select} an arbitrarily nested layout inside - * another layout. + * Layout paths can be used to: + *

    *

    - * Such layout paths can be constructed programmatically using the methods in this class. - * For instance, given the {@code taggedValues} layout instance constructed as above, we can obtain the offset, + * For instance, given the {@code taggedValues} sequence layout constructed above, we can obtain the offset, * in bytes, of the member layout named value in the first sequence element, as follows: * {@snippet lang=java : * long valueOffset = taggedValues.byteOffset(PathElement.sequenceElement(0), @@ -134,27 +141,26 @@ import jdk.internal.javac.PreviewFeature; * PathElement.groupElement("value")); * } * - * Layout paths can feature one or more free dimensions. For instance, a layout path traversing - * an unspecified sequence element (that is, where one of the path component was obtained with the - * {@link PathElement#sequenceElement()} method) features an additional free dimension, which will have to be bound at runtime. - * This is important when obtaining a {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) memory segment view var handle} - * from layouts, as in the following code: + *

    Open path elements

    + * + * Some layout path elements, said open path elements, can select multiple layouts at once. For instance, + * the open path elements {@link PathElement#sequenceElement()}, {@link PathElement#sequenceElement(long, long)} select + * an unspecified element in a sequence layout. A var handle derived from a layout path containing one or more + * open path element features additional coordinates of type {@code long}, which can be used by clients to bind + * the open elements in the path: * * {@snippet lang=java : * VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(), * PathElement.groupElement("value")); + * MemorySegment valuesSegment = ... + * int val = (int) valueHandle.get(valuesSegment, 2); // reads the "value" field of the third struct in the array * } * - * Since the layout path constructed in the above example features exactly one free dimension (as it doesn't specify - * which member layout named {@code value} should be selected from the enclosing sequence layout), - * it follows that the var handle {@code valueHandle} will feature an additional {@code long} - * access coordinate. - * - *

    A layout path with free dimensions can also be used to create an offset-computing method handle, using the - * {@link #byteOffset(PathElement...)} or {@link #byteOffsetHandle(PathElement...)} method. Again, free dimensions are - * translated into {@code long} parameters of the created method handle. The method handle can be used to compute the - * offsets of elements of a sequence at different indices, by supplying these indices when invoking the method handle. - * For instance: + *

    + * Open path elements also affects the creation of + * {@linkplain #byteOffsetHandle(PathElement...) offset-computing method handles}. Each open path element becomes + * an additional {@code long} parameter in the obtained method handle. This parameter can be used to specify the index + * of the sequence element whose offset is to be computed: * * {@snippet lang=java : * MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(), @@ -163,6 +169,68 @@ import jdk.internal.javac.PreviewFeature; * long offset2 = (long) offsetHandle.invokeExact(2L); // 16 * } * + *

    Dereference path elements

    + * + * A special kind of path element, called dereference path element, allows var handles obtained from + * memory layouts to follow pointers. Consider the following layout: + * + * {@snippet lang=java : + * StructLayout RECTANGLE = MemoryLayout.structLayout( + * ValueLayout.ADDRESS.withTargetLayout( + * MemoryLayout.sequenceLayout(4, + * MemoryLayout.structLayout( + * ValueLayout.JAVA_INT.withName("x"), + * ValueLayout.JAVA_INT.withName("y") + * ).withName("point") +* ) +* ).withName("points") + * ); + * } + * + * This layout is a struct layout which describe a rectangle. It contains a single field, namely {@code points}, + * an address layout whose {@linkplain AddressLayout#targetLayout() target layout} is a sequence layout of four + * struct layouts. Each struct layout describes a two-dimensional point, and is defined as a pair or + * {@link ValueLayout#JAVA_INT} coordinates, with names {@code x} and {@code y}, respectively. + *

    + * With dereference path elements, we can obtain a var handle which accesses the {@code y} coordinate of one of the + * point in the rectangle, as follows: + * + * {@snippet lang=java : + * VarHandle rectPointYs = RECTANGLE.varHandle( + * PathElement.groupElement("points"), + * PathElement.dereferenceElement(), + * PathElement.sequenceElement(), + * PathElement.groupElement("y") + * ); + * + * MemorySegment rect = ... + * int rect_y_4 = (int) rectPointYs.get(rect, 2); // rect.points[2]->y + * } + * + *

    Layout path well-formedness

    + * + * A layout path is applied to a layout {@code C_0}, also called the initial layout. Each path element in a + * layout path can be thought of as a function which updates the current layout {@code C_i-1} to some other layout + * {@code C_i}. That is, for each path element {@code E1, E2, ... En}, in a layout path {@code P}, we compute + * {@code C_i = f_i(C_i-1)}, where {@code f_i} is the selection function associated with the path element under consideration, + * denoted as {@code E_i}. The final layout {@code C_i} is also called the selected layout. + *

    + * A layout path {@code P} is considered well-formed for an initial layout {@code C_0} if all its path elements + * {@code E1, E2, ... En} are well-formed for their corresponding input layouts {@code C_0, C_1, ... C_n-1}. + * A path element {@code E} is considered well-formed for a layout {@code L} if any of the following is true: + *

    + * Any attempt to provide a layout path {@code P} that is not well-formed for an initial layout {@code C_0} will result + * in an {@link IllegalArgumentException}. + * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. * @@ -184,28 +252,23 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin Optional name(); /** - * Returns a memory layout of the same type with the same size and alignment constraint as this layout, - * but with the specified name. + * {@return a memory layout with the same characteristics as this layout, but with the given name} * * @param name the layout name. - * @return a memory layout with the given name. * @see MemoryLayout#name() */ MemoryLayout withName(String name); /** - * Returns a memory layout of the same type with the same size and alignment constraint as this layout, - * but without a name. - *

    - * This can be useful to compare two layouts that have different names, but are otherwise equal. + * {@return a memory layout with the same characteristics as this layout, but with no name} * - * @return a memory layout without a name. + * @apiNote This can be useful to compare two layouts that have different names, but are otherwise equal. * @see MemoryLayout#name() */ MemoryLayout withoutName(); /** - * Returns the alignment constraint associated with this layout, expressed in bytes. Layout alignment defines a power + * {@return the alignment constraint associated with this layout, expressed in bytes} Layout alignment defines a power * of two {@code A} which is the byte-wise alignment of the layout, where {@code A} is the number of bytes that must be aligned * for any pointer that correctly points to this layout. Thus: * @@ -217,36 +280,27 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * * If no explicit alignment constraint was set on this layout (see {@link #withByteAlignment(long)}), * then this method returns the natural alignment constraint (in bytes) associated with this layout. - * - * @return the layout alignment constraint, in bytes. */ long byteAlignment(); - /** - * Returns a memory layout of the same type with the same size and name as this layout, - * but with the specified alignment constraint (in bytes). + * {@return a memory layout with the same characteristics as this layout, but with the given + * alignment constraint (in bytes)} * * @param byteAlignment the layout alignment constraint, expressed in bytes. - * @return a memory layout with the given alignment constraint. - * @throws IllegalArgumentException if {@code byteAlignment} is not a power of two, or if it's less than 1. + * @throws IllegalArgumentException if {@code byteAlignment} is not a power of two. */ MemoryLayout withByteAlignment(long byteAlignment); - /** - * Computes the offset, in bytes, of the layout selected by the given layout path, where the path is considered rooted in this - * layout. + * Computes the offset, in bytes, of the layout selected by the given layout path, where the initial layout in the + * path is this layout. * * @param elements the layout path elements. * @return The offset, in bytes, of the layout selected by the layout path in {@code elements}. - * @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the - * layout path contains one or more path elements that select multiple sequence element indices - * (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}). - * @throws IllegalArgumentException if the layout path contains one or more dereference path elements - * (see {@link PathElement#dereferenceElement()}). - * @throws NullPointerException if either {@code elements == null}, or if any of the elements - * in {@code elements} is {@code null}. + * @throws IllegalArgumentException if the layout path is not well-formed for this layout. + * @throws IllegalArgumentException if the layout path contains one or more open path elements. + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements. */ default long byteOffset(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::offset, @@ -254,19 +308,21 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin } /** - * Creates a method handle that can be used to compute the offset, in bytes, of the layout selected - * by the given layout path, where the path is considered rooted in this layout. - * - *

    The returned method handle has a return type of {@code long}, and features as many {@code long} - * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}), - * where the order of the parameters corresponds to the order of the path elements. - * The returned method handle can be used to compute a layout offset similar to {@link #byteOffset(PathElement...)}, - * but where some sequence indices are specified only when invoking the method handle. - * - *

    The final offset returned by the method handle is computed as follows: + * Creates a method handle that computes the offset, in bytes, of the layout selected + * by the given layout path, where the initial layout in the path is this layout. + *

    + * The returned method handle has the following characteristics: + *

    + *

    + * The final offset returned by the method handle is computed as follows: * *

    {@code
    -     * byteOffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
    +     * offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
          * }
    * * where {@code x_1}, {@code x_2}, ... {@code x_n} are dynamic values provided as {@code long} @@ -274,22 +330,32 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * and {@code s_0}, {@code s_1}, ... {@code s_n} are static stride constants which are derived from * the layout path. * + * @apiNote The returned method handle can be used to compute a layout offset, similarly to {@link #byteOffset(PathElement...)}, + * but more flexibly, as some indices can be specified when invoking the method handle. + * * @param elements the layout path elements. - * @return a method handle that can be used to compute the byte offset of the layout element - * specified by the given layout path elements, when supplied with the missing sequence element indices. - * @throws IllegalArgumentException if the layout path contains one or more path elements that select - * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}). - * @throws IllegalArgumentException if the layout path contains one or more dereference path elements - * (see {@link PathElement#dereferenceElement()}). + * @return a method handle that computes the offset, in bytes, of the layout selected by the given layout path. + * @throws IllegalArgumentException if the layout path is not well-formed for this layout. + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements. */ default MethodHandle byteOffsetHandle(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::offsetHandle, - EnumSet.of(PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); + EnumSet.of(PathKind.DEREF_ELEMENT), elements); } /** - * 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. + * Creates a var handle that accesses a memory segment at the offset selected by the given layout path, + * where the initial layout in the path is this layout. + *

    + * The returned var handle has the following characteristics: + *

    *

    * The final address accessed by the returned var handle can be computed as follows: * @@ -300,7 +366,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * 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: + * of heap segments is virtualized. The {@code offset} value 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)
    @@ -311,14 +377,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
          * and {@code s_1}, {@code s_2}, ... {@code s_n} are static stride constants which are derived from
          * the layout path.
          * 

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

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

    {@code
          * address_1 = base(segment) + offset_1
    @@ -336,16 +402,13 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
          * (e.g. those at addresses {@code address_1}, {@code address_2}, ...,  {@code address_k-1} are performed using the
          * {@link VarHandle.AccessMode#GET} access mode.
          *
    -     * @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every
    -     * unspecified sequence access component contained in this layout path. Moreover, the resulting var handle
    -     * features certain access mode restrictions, which are common to all memory segment view handles.
    +     * @apiNote The resulting var handle features certain access mode restrictions, which are common to all
    +     * {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) memory segment view handles}.
          *
          * @param elements the layout path elements.
    -     * @return a var handle which can be used to access a memory segment at the (possibly nested) layout selected by the layout path in {@code elements}.
    -     * @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraint.
    -     * @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}).
    -     * @throws IllegalArgumentException if the layout path in {@code elements} contains a {@linkplain PathElement#dereferenceElement()
    -     * dereference path element} for an address layout that has no {@linkplain AddressLayout#targetLayout() target layout}.
    +     * @return a var handle that accesses a memory segment at the offset selected by the given layout path.
    +     * @throws IllegalArgumentException if the layout path is not well-formed for this layout.
    +     * @throws IllegalArgumentException if the layout selected by the provided path is not a {@linkplain ValueLayout value layout}.
          * @see MethodHandles#memorySegmentViewVarHandle(ValueLayout)
          */
         default VarHandle varHandle(PathElement... elements) {
    @@ -355,54 +418,47 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
     
         /**
          * Creates a method handle which, given a memory segment, returns a {@linkplain MemorySegment#asSlice(long,long) slice}
    -     * corresponding to the layout selected by the given layout path, where the path is considered rooted in this layout.
    -     *
    -     * 

    The returned method handle has a return type of {@code MemorySegment}, features a {@code MemorySegment} - * parameter as leading parameter representing the segment to be sliced, and features as many trailing {@code long} - * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}), - * where the order of the parameters corresponds to the order of the path elements. - * The returned method handle can be used to create a slice similar to using {@link MemorySegment#asSlice(long, long)}, - * but where the offset argument is dynamically compute based on indices specified when invoking the method handle. - * - *

    The offset of the returned segment is computed as follows: - * - *

    {@code
    -     * byteOffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
    -     * }
    - * - * where {@code x_1}, {@code x_2}, ... {@code x_n} are dynamic values provided as {@code long} - * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} are static offset constants - * and {@code s_1}, {@code s_2}, ... {@code s_n} are static stride constants which are derived from - * the layout path. - * - *

    After the offset is computed, the returned segment is created as if by calling: + * corresponding to the layout selected by the given layout path, where the initial layout in the path is this layout. + *

    + * The returned method handle has the following characteristics: + *

      + *
    • its return type is {@code MemorySegment};
    • + *
    • it has a leading parameter of type {@code MemorySegment}, corresponding to the memory segment + * to be sliced;
    • + *
    • it has as zero or more parameters of type {@code long}, one for each open path element + * in the provided layout path. The order of these parameters corresponds to the order in which the open path + * elements occur in the provided layout path. + *
    + *

    + * The offset of the returned segment is computed as follows: * {@snippet lang=java : - * segment.asSlice(offset, layout.byteSize()); + * long offset = byteOffset(elements); + * long size = select(elements).byteSize(); + * MemorySegment slice = segment.asSlice(offset, size); * } * - * where {@code segment} is the segment to be sliced, and where {@code layout} is the layout selected by the given - * layout path, as per {@link MemoryLayout#select(PathElement...)}. + * @apiNote The returned method handle can be used to obtain a memory segment slice, similarly to {@link MemorySegment#asSlice(long, long)}, + * but more flexibly, as some indices can be specified when invoking the method handle. * * @param elements the layout path elements. - * @return a method handle which can be used to create a slice of the selected layout element, given a segment. - * @throws IllegalArgumentException if the layout path contains one or more dereference path elements - * (see {@link PathElement#dereferenceElement()}). + * @return a method handle which is used to slice a memory segment at the offset selected by the given layout path. + * @throws IllegalArgumentException if the layout path is not well-formed for this layout. + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements. */ default MethodHandle sliceHandle(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::sliceHandle, - Set.of(), elements); + Set.of(PathKind.DEREF_ELEMENT), elements); } /** - * Selects the layout from a path rooted in this layout. + * Returns the layout selected from the provided path, where the initial layout in the path is this layout. * * @param elements the layout path elements. * @return the layout selected by the layout path in {@code elements}. - * @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, - * or if the layout path contains one or more path elements that select one or more sequence element indices - * (see {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}). - * @throws IllegalArgumentException if the layout path contains one or more dereference path elements - * (see {@link PathElement#dereferenceElement()}). + * @throws IllegalArgumentException if the layout path is not well-formed for this layout. + * @throws IllegalArgumentException if the layout path contains one or more dereference path elements. + * @throws IllegalArgumentException if the layout path contains one or more path elements that select one or more + * sequence element indices, such as {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}). */ default MemoryLayout select(PathElement... elements) { return computePathOp(LayoutPath.rootPath(this), LayoutPath::layout, @@ -424,12 +480,15 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin /** * An element in a layout path. There - * are two kinds of path elements: group path elements and sequence path elements. Group - * path elements are used to select a named member layout within a {@link GroupLayout}. Sequence - * path elements are used to select a sequence element layout within a {@link SequenceLayout}; selection - * of sequence element layout can be explicit (see {@link PathElement#sequenceElement(long)}) or - * implicit (see {@link PathElement#sequenceElement()}). When a path uses one or more implicit - * sequence path elements, it acquires additional free dimensions. + * are three kinds of path elements: + *

      + *
    • group path elements, used to select a member layout within a {@link GroupLayout}, either by name or by index;
    • + *
    • sequence path elements, used to select one or more sequence element layouts within a {@link SequenceLayout}; and
    • + *
    • dereference path elements, used to dereference + * an address layout as its target layout.
    • + *
    + * Sequence path elements selecting more than one sequence element layout are called + * open path elements. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. @@ -441,15 +500,13 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin /** * Returns a path element which selects a member layout with the given name in a group layout. - * The path element returned by this method does not alter the number of free dimensions of any path - * that is combined with such element. * * @implSpec in case multiple group elements with a matching name exist, the path element returned by this * method will select the first one; that is, the group element with the lowest offset from current path is selected. * In such cases, using {@link #groupElement(long)} might be preferable. * - * @param name the name of the group element to be selected. - * @return a path element which selects the group element with the given name. + * @param name the name of the member layout to be selected. + * @return a path element which selects the group member layout with the given name. */ static PathElement groupElement(String name) { Objects.requireNonNull(name); @@ -459,11 +516,9 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin /** * Returns a path element which selects a member layout with the given index in a group layout. - * The path element returned by this method does not alter the number of free dimensions of any path - * that is combined with such element. * - * @param index the index of the group element to be selected. - * @return a path element which selects the group element with the given index. + * @param index the index of the member layout element to be selected. + * @return a path element which selects the group member layout with the given index. * @throws IllegalArgumentException if {@code index < 0}. */ static PathElement groupElement(long index) { @@ -476,8 +531,6 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin /** * Returns a path element which selects the element layout at the specified position in a sequence layout. - * The path element returned by this method does not alter the number of free dimensions of any path - * that is combined with such element. * * @param index the index of the sequence element to be selected. * @return a path element which selects the sequence element layout with the given index. @@ -492,24 +545,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin } /** - * Returns a path element which selects the element layout in a range of positions in a sequence layout. - * The range is expressed as a pair of starting index (inclusive) {@code S} and step factor (which can also be negative) - * {@code F}. + * Returns an open path element which selects the element + * layout in a range of positions in a sequence layout. The range is expressed as a pair of starting + * index (inclusive) {@code S} and step factor (which can also be negative) {@code F}. *

    - * If a path with free dimensions {@code n} is combined with the path element returned by this method, - * the number of free dimensions of the resulting path will be {@code 1 + n}. If the free dimension associated - * with this path is bound by an index {@code I}, the resulting accessed offset can be obtained with the following - * formula: - * - *

    {@code
    -         * E * (S + I * F)
    -         * }
    - * - * where {@code E} is the size (in bytes) of the sequence element layout. - *

    - * Additionally, if {@code C} is the sequence element count, it follows that {@code 0 <= I < B}, - * where {@code B} is computed as follows: - * + * The exact sequence element selected by this layout is expressed as an index {@code I}. If {@code C} is the + * sequence element count, it follows that {@code 0 <= I < B}, where {@code B} is computed as follows: *

      *
    • if {@code F > 0}, then {@code B = ceilDiv(C - S, F)}
    • *
    • if {@code F < 0}, then {@code B = ceilDiv(-(S + 1), -F)}
    • @@ -532,20 +573,11 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin } /** - * Returns a path element which selects an unspecified element layout in a sequence layout. + * Returns an open path element which selects an unspecified + * element layout in a sequence layout. *

      - * If a path with free dimensions {@code n} is combined with the path element returned by this method, - * the number of free dimensions of the resulting path will be {@code 1 + n}. If the free dimension associated - * with this path is bound by an index {@code I}, the resulting accessed offset can be obtained with the following - * formula: - * - *

      {@code
      -         * E * I
      -         * }
      - * - * where {@code E} is the size (in bytes) of the sequence element layout. - *

      - * Additionally, if {@code C} is the sequence element count, it follows that {@code 0 <= I < C}. + * The exact sequence element selected by this layout is expressed as an index {@code I}. If {@code C} is the + * sequence element count, it follows that {@code 0 <= I < C}. * * @return a path element which selects an unspecified sequence element layout. */ @@ -557,10 +589,6 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin /** * Returns a path element which dereferences an address layout as its * {@linkplain AddressLayout#targetLayout() target layout} (where set). - * The path element returned by this method does not alter the number of free dimensions of any path - * that is combined with such element. Using this path layout to dereference an address layout - * that has no target layout results in an {@link IllegalArgumentException} (e.g. when - * a var handle is {@linkplain #varHandle(PathElement...) obtained}). * * @return a path element which dereferences an address layout. */ @@ -577,11 +605,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * conditions must be satisfied: *

        *
      • two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order}, - * and {@linkplain ValueLayout#carrier() carrier}
      • + * and {@linkplain ValueLayout#carrier() carrier}. Additionally, two address layouts are considered equal if they + * also have the same {@linkplain AddressLayout#targetLayout() target layout}; *
      • 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
      • + * if their element layouts (see {@link SequenceLayout#elementLayout()}) 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
      • + * {@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. @@ -601,7 +630,10 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin String toString(); /** - * Creates a padding layout with the given byte size and a byte-alignment of one. + * Creates a padding layout with the given byte size. The alignment constraint of the returned layout + * is 1. As such, regardless of its size, in the absence of an {@linkplain #withByteAlignment(long) explicit} + * alignment constraint, a padding layout does not affect the natural alignment of the group or sequence layout + * it is nested into. * * @param byteSize the padding size (expressed in bytes). * @return the new selector layout. @@ -617,14 +649,15 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @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 } is negative. + * @throws IllegalArgumentException if {@code elementCount} is negative. + * @throws IllegalArgumentException if {@code elementLayout.byteSize() * elementCount} overflows. * @throws IllegalArgumentException if {@code elementLayout.byteSize() % elementLayout.byteAlignment() != 0}. */ static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) { MemoryLayoutUtil.requireNonNegative(elementCount); Objects.requireNonNull(elementLayout); Utils.checkElementAlignment(elementLayout, "Element layout size is not multiple of alignment"); - return wrapOverflow(() -> + return Utils.wrapOverflow(() -> SequenceLayoutImpl.of(elementCount, elementLayout)); } @@ -678,7 +711,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin */ static StructLayout structLayout(MemoryLayout... elements) { Objects.requireNonNull(elements); - return wrapOverflow(() -> + return Utils.wrapOverflow(() -> StructLayoutImpl.of(Stream.of(elements) .map(Objects::requireNonNull) .toList())); @@ -696,12 +729,4 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin .map(Objects::requireNonNull) .toList()); } - - private static L wrapOverflow(Supplier layoutSupplier) { - try { - return layoutSupplier.get(); - } catch (ArithmeticException ex) { - throw new IllegalArgumentException("Layout size exceeds Long.MAX_VALUE"); - } - } } 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 36e2decc4dd..c074cc8f896 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -108,7 +108,7 @@ import jdk.internal.vm.annotation.ForceInline; *

      * Finally, access operations on a memory segment can be subject to additional thread-confinement checks. * Heap segments can be accessed from any thread. Conversely, native segments can only be accessed compatibly with the - * confinement characteristics of the arena used to obtain them. + * confinement characteristics of the arena used to obtain them. * *

      Accessing memory segments

      * @@ -141,7 +141,7 @@ import jdk.internal.vm.annotation.ForceInline; * .findStatic(Math.class, "multiplyExact", * MethodType.methodType(long.class, long.class, long.class)); * intHandle = MethodHandles.filterCoordinates(intHandle, 1, - * MethodHandles.insertArguments(multiplyExact, 0, 4L)); + * MethodHandles.insertArguments(multiplyExact, 0, ValueLayout.JAVA_INT.byteSize())); * int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 * } * @@ -260,9 +260,10 @@ import jdk.internal.vm.annotation.ForceInline; * (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: + * aligned physical address (except for {@code long[]} and {@code double[]}, where alignment is platform-dependent, as explained + * below). 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 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 @@ -278,10 +279,10 @@ import jdk.internal.vm.annotation.ForceInline; * 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 long[]} array will be 4-byte aligned (e.g. 1000) on 32-bit platforms, + *
      • The starting physical address of a {@code long[]} array will be 4-byte aligned (e.g. 1004) on 32-bit platforms, * so that successive long elements occur at 4-byte aligned addresses (e.g., 1004, 1008, 1012, 1016, etc.) On 32-bit * platforms, a heap segment backed by a {@code long[]} array 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 + * alignment constraint, because the target addresses (1004, 1008, 1012, 1016) 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.
      • *
      @@ -440,11 +441,17 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * {@return the address of this memory segment} + * + * @apiNote When using this method to pass a segment address to some external operation (e.g. a JNI function), + * clients must ensure that the segment is kept reachable + * for the entire duration of the operation. A failure to do so might result in the premature deallocation of the + * region of memory backing the memory segment, in case the segment has been allocated with an + * {@linkplain Arena#ofAuto() automatic arena}. */ long address(); /** - * Returns the Java object stored in the on-heap memory region backing this memory segment, if any. For instance, if this + * Returns the Java object stored in the on-heap region of memory backing this memory segment, if any. For instance, if this * memory segment is a heap segment created with the {@link #ofArray(byte[])} factory method, this method will return the * {@code byte[]} object which was used to obtain the segment. This method returns an empty {@code Optional} value * if either this segment is a {@linkplain #isNative() native} segment, or if this segment is {@linkplain #isReadOnly() read-only}. @@ -515,7 +522,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { *

      * Equivalent to the following code: * {@snippet lang=java : - * asSlice(offset, layout.byteSize(), 1); + * asSlice(offset, newSize, 1); * } * * @see #asSlice(long, long, long) @@ -523,7 +530,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param offset The new segment base offset (relative to the address of this segment), specified in bytes. * @param newSize The new segment size, specified in bytes. * @return a slice of this memory segment. - * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset} + * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, + * or {@code newSize > byteSize() - offset} */ MemorySegment asSlice(long offset, long newSize); @@ -535,9 +543,11 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param newSize The new segment size, specified in bytes. * @param byteAlignment The alignment constraint (in bytes) of the returned slice. * @return a slice of this memory segment. - * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset} + * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, + * or {@code newSize > byteSize() - offset} * @throws IllegalArgumentException if this segment cannot be accessed at {@code offset} under * the provided alignment constraint. + * @throws IllegalArgumentException if {@code byteAlignment <= 0}, or if {@code byteAlignment} is not a power of 2. */ MemorySegment asSlice(long offset, long newSize, long byteAlignment); @@ -554,8 +564,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * * @param offset The new segment base offset (relative to the address of this segment), specified in bytes. * @param layout The layout of the segment slice. - * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > layout.byteSize()}, - * {@code newSize < 0}, or {@code newSize > layout.byteSize() - offset} + * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, + * or {@code layout.byteSize() > byteSize() - offset} * @throws IllegalArgumentException if this segment cannot be accessed at {@code offset} under * the alignment constraint specified by {@code layout}. * @return a slice of this memory segment. @@ -612,7 +622,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * Clients can specify an optional cleanup action that should be executed when the provided scope becomes * invalid. This cleanup action receives a fresh memory segment that is obtained from this segment as follows: * {@snippet lang=java : - * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()); + * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()) + * .reinterpret(byteSize()); * } * That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive, * and is accessible from any thread. The size of the segment accepted by the cleanup action is {@link #byteSize()}. @@ -631,8 +642,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @param arena the arena to be associated with the returned segment. * @param cleanup the cleanup action that should be executed when the provided arena is closed (can be {@code null}). * @return a new memory segment with unbounded size. - * @throws IllegalArgumentException if {@code newSize < 0}. - * @throws IllegalStateException if {@code scope.isAlive() == false}. + * @throws IllegalStateException if {@code arena.scope().isAlive() == false}. * @throws UnsupportedOperationException if this segment is not a {@linkplain #isNative() native} segment. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. */ @@ -651,7 +661,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * Clients can specify an optional cleanup action that should be executed when the provided scope becomes * invalid. This cleanup action receives a fresh memory segment that is obtained from this segment as follows: * {@snippet lang=java : - * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()); + * MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address()) + * .reinterpret(newSize); * } * That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive, * and is accessible from any thread. The size of the segment accepted by the cleanup action is {@code newSize}. @@ -674,7 +685,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * that of the provided arena. * @throws UnsupportedOperationException if this segment is not a {@linkplain #isNative() native} segment. * @throws IllegalArgumentException if {@code newSize < 0}. - * @throws IllegalStateException if {@code scope.isAlive() == false}. + * @throws IllegalStateException if {@code arena.scope().isAlive() == false}. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. */ @CallerSensitive @@ -718,7 +729,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * at least two slices {@code L1} (from {@code S1}) and {@code L2} (from {@code S2}) that are backed by the * same region of memory. As such, it is not possible for a * {@linkplain #isNative() native} segment to overlap with a heap segment; in - * this case, or when no overlap occurs, {@code null} is returned. + * this case, or when no overlap occurs, an empty {@code Optional} is returned. * * @param other the segment to test for an overlap with this segment. * @return a slice of this segment (where overlapping occurs). @@ -735,7 +746,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * can be computed as follows: * * {@snippet lang=java : - * other.address() - segment.address() + * other.address() - address() * } * * If the segments share the same address, {@code 0} is returned. If @@ -744,37 +755,35 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * * @param other the segment to retrieve an offset to. * @throws UnsupportedOperationException if the two segments cannot be compared, e.g. because they are of - * a different kind, or because they are backed by different Java arrays. + * different kinds, or because they are backed by different Java arrays. * @return the relative offset, in bytes, of the provided segment. */ long segmentOffset(MemorySegment other); /** - * Fills a value into this memory segment. + * Fills the contents of this memory segment with the given value. *

      - * More specifically, the given value is filled into each address of this + * More specifically, the given value is written into each address of this * segment. Equivalent to (but likely more efficient than) the following code: * * {@snippet lang=java : - * var byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) - * .varHandle(MemoryLayout.PathElement.sequenceElement()); - * for (long l = 0; l < segment.byteSize(); l++) { - * byteHandle.set(segment.address(), l, value); + * for (long offset = 0; offset < segment.byteSize(); offset++) { + * byteHandle.set(ValueLayout.JAVA_BYTE, offset, value); * } * } * - * without any regard or guarantees on the ordering of particular memory + * But without any regard or guarantees on the ordering of particular memory * elements being set. *

      - * Fill can be useful to initialize or reset the memory of a segment. + * This method can be useful to initialize or reset the contents of a memory segment. * - * @param value the value to fill into this segment - * @return this memory segment + * @param value the value to write into this segment. + * @return this memory segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. - * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}). + * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ MemorySegment fill(byte value); @@ -797,7 +806,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code src.isAccessibleBy(T) == false}. - * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}). + * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. * @return this segment. */ default MemorySegment copyFrom(MemorySegment src) { @@ -820,9 +829,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * valid for the larger segment. Otherwise, there is no mismatch and {@code * -1} is returned. * - * @param other the segment to be tested for a mismatch with this segment + * @param other the segment to be tested for a mismatch with this segment. * @return the relative offset, in bytes, of the first mismatch between this - * and the given other segment, otherwise -1 if no mismatch + * and the given other segment, otherwise -1 if no mismatch. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, @@ -930,23 +939,24 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * 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 segment, the resulting buffer is - * direct (see {@link ByteBuffer#isDirect()}). + * the properties of this segment. More specifically, the resulting buffer has the following characteristics: + *

        + *
      • It is {@linkplain ByteBuffer#isReadOnly() read-only}, if this segment is a + * {@linkplain #isReadOnly() read-only segment};
      • + *
      • Its {@linkplain ByteBuffer#position() position} is set to zero; + *
      • Its {@linkplain ByteBuffer#capacity() capacity} and {@linkplain ByteBuffer#limit() limit} + * are both set to this segment' {@linkplain MemorySegment#byteSize() size}. For this reason, a byte buffer + * cannot be returned if this segment's size is greater than {@link Integer#MAX_VALUE};
      • + *
      • It is a {@linkplain ByteBuffer#isDirect() direct buffer}, if this is a native segment.
      • + *
      *

      - * The returned buffer's position (see {@link ByteBuffer#position()}) is initially set to zero, while - * the returned buffer's capacity and limit (see {@link ByteBuffer#capacity()} and {@link ByteBuffer#limit()}, respectively) - * are set to this segment' size (see {@link MemorySegment#byteSize()}). For this reason, a byte buffer cannot be - * 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 + * The life-cycle of the returned buffer is tied to that of this segment. That is, accessing the returned buffer * after the scope associated with this segment is no longer {@linkplain Scope#isAlive() alive}, will * throw an {@link IllegalStateException}. Similarly, accessing the returned buffer from a thread {@code T} * such that {@code isAccessible(T) == false} will throw a {@link WrongThreadException}. *

      - * If this segment is accessible from a single thread, calling read/write I/O - * operations on the resulting buffer might result in an unspecified exception being thrown. Examples of such problematic operations are + * If this segment is {@linkplain #isAccessibleBy(Thread) accessible} from a single thread, calling read/write I/O + * operations on the resulting buffer might result in unspecified exceptions being thrown. Examples of such problematic operations are * {@link java.nio.channels.AsynchronousSocketChannel#read(ByteBuffer)} and * {@link java.nio.channels.AsynchronousSocketChannel#write(ByteBuffer)}. *

      @@ -955,7 +965,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * * @return a {@link ByteBuffer} view of this memory segment. * @throws UnsupportedOperationException if this segment cannot be mapped onto a {@link ByteBuffer} instance, - * e.g. because it models a heap-based segment that is not based on a {@code byte[]}), or if its size is greater + * e.g. if it is a heap segment backed by an array other than {@code byte[]}), or if its size is greater * than {@link Integer#MAX_VALUE}. */ ByteBuffer asByteBuffer(); @@ -984,7 +994,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code short[]} instance, - * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE} + * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer.MAX_VALUE} */ short[] toArray(ValueLayout.OfShort elementLayout); @@ -998,7 +1008,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code char[]} instance, - * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE}. + * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer.MAX_VALUE}. */ char[] toArray(ValueLayout.OfChar elementLayout); @@ -1012,7 +1022,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code int[]} instance, - * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. + * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer.MAX_VALUE}. */ int[] toArray(ValueLayout.OfInt elementLayout); @@ -1026,7 +1036,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code float[]} instance, - * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. + * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer.MAX_VALUE}. */ float[] toArray(ValueLayout.OfFloat elementLayout); @@ -1040,7 +1050,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code long[]} instance, - * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. + * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer.MAX_VALUE}. */ long[] toArray(ValueLayout.OfLong elementLayout); @@ -1054,7 +1064,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code double[]} instance, - * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. + * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer.MAX_VALUE}. */ double[] toArray(ValueLayout.OfDouble elementLayout); @@ -1069,7 +1079,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @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 offset < 0} or {@code S + offset > byteSize()}, where {@code S} is the size of the UTF-8 + * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code offset > byteSize() - S}, where {@code S} is the size of the UTF-8 * string (including the terminator character). * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not * {@linkplain Scope#isAlive() alive}. @@ -1095,7 +1105,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @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 offset < 0} or {@code str.getBytes().length() + offset >= byteSize()}. + * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code offset > byteSize() - str.getBytes().length() + 1}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, @@ -1110,7 +1120,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * 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 Buffer#isReadOnly() read-only}, the resulting segment will also be + * If the buffer is {@linkplain Buffer#isReadOnly() read-only}, the resulting segment is also * {@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. *

      @@ -1135,7 +1145,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given byte array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param byteArray the primitive array backing the heap memory segment. @@ -1147,7 +1157,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given char array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param charArray the primitive array backing the heap segment. @@ -1159,7 +1169,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given short array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param shortArray the primitive array backing the heap segment. @@ -1171,7 +1181,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given int array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param intArray the primitive array backing the heap segment. @@ -1183,7 +1193,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given float array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param floatArray the primitive array backing the heap segment. @@ -1195,7 +1205,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given long array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param longArray the primitive array backing the heap segment. @@ -1207,7 +1217,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a heap segment backed by the on-heap region of memory that holds the given double array. - * The scope of the returned segment is a fresh scope that is always alive, and keeps the given byte array reachable. + * The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable. * The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero. * * @param doubleArray the primitive array backing the heap segment. @@ -1224,7 +1234,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** * Creates a zero-length native segment from the given {@linkplain #address() address value}. - * The returned segment is always accessible, from any thread. + * The returned segment is associated with a scope that is always alive, and is accessible from any thread. *

      * On 32-bit platforms, the given address value will be normalized such that the * highest-order ("leftmost") 32 bits of the {@link MemorySegment#address() address} @@ -1268,10 +1278,10 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code dstSegment.isAccessibleBy(T) == false}. - * @throws IndexOutOfBoundsException if {@code srcOffset + bytes > srcSegment.byteSize()} or if - * {@code dstOffset + bytes > dstSegment.byteSize()}, or if either {@code srcOffset}, {@code dstOffset} + * @throws IndexOutOfBoundsException if {@code srcOffset > srcSegment.byteSize() - bytes} or if + * {@code dstOffset > dstSegment.byteSize() - bytes}, or if either {@code srcOffset}, {@code dstOffset} * or {@code bytes} are {@code < 0}. - * @throws UnsupportedOperationException if the destination segment is read-only (see {@link #isReadOnly()}). + * @throws UnsupportedOperationException if {@code dstSegment} is {@linkplain #isReadOnly() read-only}. */ @ForceInline static void copy(MemorySegment srcSegment, long srcOffset, @@ -1316,10 +1326,10 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code dstSegment().isAccessibleBy(T) == false}. - * @throws IndexOutOfBoundsException if {@code srcOffset + (elementCount * S) > srcSegment.byteSize()} or if - * {@code dstOffset + (elementCount * S) > dstSegment.byteSize()}, where {@code S} is the byte size - * of the element layouts, or if either {@code srcOffset}, {@code dstOffset} or {@code elementCount} are {@code < 0}. - * @throws UnsupportedOperationException if the destination segment is read-only (see {@link #isReadOnly()}). + * @throws UnsupportedOperationException if {@code dstSegment} is {@linkplain #isReadOnly() read-only}. + * @throws IndexOutOfBoundsException if {@code elementCount * srcLayout.byteSize()} or {@code elementCount * dtsLayout.byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - (elementCount * dstLayout.byteSize())}. + * @throws IndexOutOfBoundsException if either {@code srcOffset}, {@code dstOffset} or {@code elementCount} are {@code < 0}. */ @ForceInline static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long srcOffset, @@ -1344,8 +1354,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default byte get(ValueLayout.OfByte layout, long offset) { @@ -1364,8 +1373,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1385,8 +1393,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default boolean get(ValueLayout.OfBoolean layout, long offset) { @@ -1405,8 +1412,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1426,8 +1432,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default char get(ValueLayout.OfChar layout, long offset) { @@ -1446,8 +1451,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1467,8 +1471,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default short get(ValueLayout.OfShort layout, long offset) { @@ -1487,8 +1490,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1508,8 +1510,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default int get(ValueLayout.OfInt layout, long offset) { @@ -1528,8 +1529,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1549,8 +1549,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default float get(ValueLayout.OfFloat layout, long offset) { @@ -1569,8 +1568,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1590,8 +1588,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default long get(ValueLayout.OfLong layout, long offset) { @@ -1610,8 +1607,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1631,8 +1627,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default double get(ValueLayout.OfDouble layout, long offset) { @@ -1651,8 +1646,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1664,7 +1658,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in * a native segment, associated with a fresh scope that is always alive. Under normal conditions, * the size of the returned segment is {@code 0}. However, if the provided address layout has a - * {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the returned segment + * {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment * is set to {@code T.byteSize()}. * @param layout the layout of the region of memory to be read. * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. @@ -1678,8 +1672,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if provided address layout has a {@linkplain AddressLayout#targetLayout() target layout} * {@code T}, and the address of the returned segment * incompatible with the alignment constraint in {@code T}. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ @ForceInline default MemorySegment get(AddressLayout layout, long offset) { @@ -1698,8 +1691,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. * @throws UnsupportedOperationException if {@code value} is not a {@linkplain #isNative() native} segment. */ @@ -1722,8 +1714,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default byte getAtIndex(ValueLayout.OfByte layout, long index) { @@ -1746,8 +1738,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default boolean getAtIndex(ValueLayout.OfBoolean layout, long index) { @@ -1770,8 +1762,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default char getAtIndex(ValueLayout.OfChar layout, long index) { @@ -1794,8 +1786,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1819,8 +1811,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default short getAtIndex(ValueLayout.OfShort layout, long index) { @@ -1843,8 +1835,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1869,8 +1861,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1894,8 +1886,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1919,8 +1911,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default int getAtIndex(ValueLayout.OfInt layout, long index) { @@ -1943,8 +1935,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -1968,8 +1960,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default float getAtIndex(ValueLayout.OfFloat layout, long index) { @@ -1992,8 +1984,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -2017,8 +2009,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default long getAtIndex(ValueLayout.OfLong layout, long index) { @@ -2041,8 +2033,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -2066,8 +2058,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default double getAtIndex(ValueLayout.OfDouble layout, long index) { @@ -2090,8 +2082,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline @@ -2105,7 +2097,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in * a native segment, associated with a fresh scope that is always alive. Under normal conditions, * the size of the returned segment is {@code 0}. However, if the provided address layout has a - * {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the returned segment + * {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment * is set to {@code T.byteSize()}. * @param layout the layout of the region of memory to be read. * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation @@ -2121,8 +2113,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if provided address layout has a {@linkplain AddressLayout#targetLayout() target layout} * {@code T}, and the address of the returned segment * incompatible with the alignment constraint in {@code T}. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ @ForceInline default MemorySegment getAtIndex(AddressLayout layout, long index) { @@ -2145,8 +2137,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IllegalArgumentException if the access operation is * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the - * memory segment. + * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. * @throws UnsupportedOperationException if {@code value} is not a {@linkplain #isNative() native} segment. */ @@ -2162,17 +2154,17 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * 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: *

        - *
      • {@code s1.array().equals(s2.array())}, that is, the two segments must be of the same kind; + *
      • {@code s1.heapBase().equals(s2.heapBase())}, 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; + * the same on-heap {@linkplain #heapBase() Java object}; *
      • {@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.
      • + * to the same offset inside their associated {@linkplain #heapBase() Java object}. *
      * @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. 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. + * to check whether two segments have the same 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. @@ -2204,10 +2196,15 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code srcSegment().isAccessibleBy(T) == false}. - * @throws IllegalArgumentException if {@code dstArray} is not an array, or if it is an array but whose type is not supported, - * 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 constraint in the source element layout, - * or if the destination element layout alignment is greater than its size. + * @throws IllegalArgumentException if {@code dstArray} is not an array, or if it is an array but whose type is not supported. + * @throws IllegalArgumentException if the destination array component type does not match {@code srcLayout.carrier()}. + * @throws IllegalArgumentException if {@code offset} is incompatible + * with the alignment constraint in the source element layout. + * @throws IllegalArgumentException if {@code srcLayout.byteAlignment() > srcLayout.byteSize()}. + * @throws IndexOutOfBoundsException if {@code elementCount * srcLayout.byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code srcOffset > srcSegment.byteSize() - (elementCount * srcLayout.byteSize())}. + * @throws IndexOutOfBoundsException if {@code dstIndex > dstArray.length - elementCount}. + * @throws IndexOutOfBoundsException if either {@code srcOffset}, {@code dstIndex} or {@code elementCount} are {@code < 0}. */ @ForceInline static void copy( @@ -2238,10 +2235,16 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code dstSegment().isAccessibleBy(T) == false}. - * @throws IllegalArgumentException if {@code srcArray} is not an array, or if it is an array but whose type is not supported, - * 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 constraint in the destination element layout, - * or if the destination element layout alignment is greater than its size. + * @throws IllegalArgumentException if {@code srcArray} is not an array, or if it is an array but whose type is not supported. + * @throws IllegalArgumentException if the source array component type does not match {@code srcLayout.carrier()}. + * @throws IllegalArgumentException if {@code offset} is incompatible + * with the alignment constraint in the source element layout. + * @throws IllegalArgumentException if {@code dstLayout.byteAlignment() > dstLayout.byteSize()}. + * @throws UnsupportedOperationException if {@code dstSegment} is {@linkplain #isReadOnly() read-only}. + * @throws IndexOutOfBoundsException if {@code elementCount * dstLayout.byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - (elementCount * dstLayout.byteSize())}. + * @throws IndexOutOfBoundsException if {@code srcIndex > srcArray.length - elementCount}. + * @throws IndexOutOfBoundsException if either {@code srcIndex}, {@code dstOffset} or {@code elementCount} are {@code < 0}. */ @ForceInline static void copy( 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 54c070001cc..cd7ae75329e 100644 --- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java +++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java @@ -38,16 +38,21 @@ import jdk.internal.javac.PreviewFeature; /** * An object that may be used to allocate {@linkplain MemorySegment memory segments}. Clients implementing this interface - * must implement the {@link #allocate(long, long)} method. This interface defines several default methods + * must implement the {@link #allocate(long, long)} method. A segment allocator defines several methods * which can be useful to create segments from several kinds of Java values such as primitives and arrays. - * This interface is a {@linkplain FunctionalInterface functional interface}: clients can easily obtain a new segment allocator - * by using either a lambda expression or a method reference. *

      - * This interface also defines factories for commonly used allocators: + * {@code SegmentAllocator} is a {@linkplain FunctionalInterface functional interface}. Clients can easily obtain a new + * segment allocator by using either a lambda expression or a method reference: + * + * {@snippet lang=java : + * SegmentAllocator autoAllocator = (byteSize, byteAlignment) -> Arena.ofAuto().allocate(byteSize, byteAlignment); + * } + *

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

        *
      • {@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) + *
      • {@link #prefixAllocator(MemorySegment)} obtains an allocator which wraps a segment * and recycles its content upon each new allocation request.
      • *
      *

      @@ -55,7 +60,15 @@ import jdk.internal.javac.PreviewFeature; * the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance, * {@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. + * the allocator parameter tells the linker where to store the return value of the foreign function. + * + * @apiNote Unless otherwise specified, the {@link #allocate(long, long)} method is not thread-safe. + * Furthermore, memory segments allocated by a segment allocator can be associated with different + * lifetimes, and can even be backed by overlapping regions of memory. For these reasons, clients should generally + * only interact with a segment allocator they own. + *

      + * Clients should consider using an {@linkplain Arena arena} instead, which, provides strong thread-safety, + * lifetime and non-overlapping guarantees. */ @FunctionalInterface @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) @@ -311,6 +324,7 @@ public interface SegmentAllocator { * @param elementLayout the array element layout. * @param count the array element count. * @return a segment for the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteSize() * count} overflows. * @throws IllegalArgumentException if {@code count < 0}. */ default MemorySegment allocateArray(MemoryLayout elementLayout, long count) { @@ -338,7 +352,7 @@ public interface SegmentAllocator { * @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 byteSize < 0}, {@code byteAlignment <= 0}, - * or if {@code alignmentBytes} is not a power of 2. + * or if {@code byteAlignment} is not a power of 2. */ MemorySegment allocate(long byteSize, long byteAlignment); @@ -347,8 +361,9 @@ public interface SegmentAllocator { * 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. *

      - * 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. + * The returned allocator throws {@link IndexOutOfBoundsException} when a slice of the provided + * segment with the requested size and alignment cannot be found. + * @implNote A slicing allocator is not thread-safe. * * @param segment the segment which the returned allocator should slice from. * @return a new slicing allocator @@ -365,14 +380,15 @@ public interface SegmentAllocator { * Equivalent to (but likely more efficient than) the following code: * {@snippet lang=java : * MemorySegment segment = ... - * SegmentAllocator prefixAllocator = (size, align) -> segment.asSlice(0, size); + * SegmentAllocator prefixAllocator = (size, align) -> segment.asSlice(0, size, align); * } - *

      - * This allocator can be useful to limit allocation requests in case a client + * The returned allocator throws {@link IndexOutOfBoundsException} when a slice of the provided + * segment with the requested size and alignment cannot be found. + * + * @apiNote A prefix allocator can be useful to limit allocation requests in case a client * knows that they have fully processed the contents of the allocated segment before the subsequent allocation request * takes place. - *

      - * While the allocator returned by this method is thread-safe, concurrent access on the same recycling + * @implNote While a prefix allocator is thread-safe, concurrent access on the same recycling * allocator might cause a thread to overwrite contents written to the underlying segment by a different thread. * * @param segment the memory segment to be recycled by the returned allocator. 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 95bde61f0bd..8259e766b15 100644 --- a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java @@ -29,9 +29,9 @@ import jdk.internal.foreign.layout.SequenceLayoutImpl; import jdk.internal.javac.PreviewFeature; /** - * A compound layout that denotes a repetition of a given element layout. - * The repetition count is said to be the sequence layout's element count. A finite sequence can be thought of as a - * group layout where the sequence layout's element layout is repeated a number of times that is equal to the sequence + * A compound layout that denotes a homogeneous repetition of a given element layout. + * The repetition count is said to be the sequence layout's element count. A sequence layout can be thought of as a + * struct layout where the sequence layout's element layout is repeated a number of times that is equal to the sequence * layout's element count. In other words this layout: * * {@snippet lang=java : @@ -57,7 +57,7 @@ public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayo /** - * {@return the element layout associated with this sequence layout} + * {@return the element layout of this sequence layout} */ MemoryLayout elementLayout(); @@ -67,18 +67,17 @@ public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayo long elementCount(); /** - * Returns a sequence layout with the same element layout, alignment constraint and name as this sequence layout, - * but with the specified element count. + * {@return a sequence layout with the same characteristics of this layout, but with the given element count} * @param elementCount the new element count. - * @return a sequence layout with the given element count. - * @throws IllegalArgumentException if {@code elementCount < 0}. + * @throws IllegalArgumentException if {@code elementCount} is negative. + * @throws IllegalArgumentException if {@code elementLayout.bitSize() * elementCount} overflows. */ SequenceLayout withElementCount(long elementCount); /** - * 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 + * Rearranges the elements in this sequence layout into a multi-dimensional sequence layout. + * The resulting layout is a sequence layout where element layouts in the {@linkplain #flatten() flattened projection} + * of this sequence layout are rearranged 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. @@ -101,7 +100,7 @@ public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayo * 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 + * @return a sequence layout where element layouts in the {@linkplain #flatten() 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, @@ -112,7 +111,16 @@ public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayo /** * 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. + * is the first non-sequence layout found by inspecting (recursively, if needed) the element layout of this sequence layout: + * {@snippet lang=java : + * MemoryLayout flatElementLayout(SequenceLayout sequenceLayout) { + * return switch (sequenceLayout.elementLayout()) { + * case SequenceLayout nestedSequenceLayout -> flatElementLayout(nestedSequenceLayout); + * case MemoryLayout layout -> 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: 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 f6ef8b23725..7db7302b28d 100644 --- a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java +++ b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java @@ -55,7 +55,7 @@ import java.util.function.BiFunction; *

    • 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(AddressLayout, long, MemorySegment) stored} inside another memory segment.
    • *
    • It can be used to access the region of memory backing a global variable (this requires - * {@link MemorySegment#reinterpret(long)} () resizing} the segment first).
    • + * {@linkplain MemorySegment#reinterpret(long) resizing} the segment first). *
    * *

    Obtaining a symbol lookup

    @@ -65,7 +65,7 @@ import java.util.function.BiFunction; * The library is loaded if not already loaded. The symbol lookup, which is known as a library lookup, and its * lifetime is controlled by an {@linkplain Arena arena}. For instance, if the provided arena is a * confined arena, the library associated with the symbol lookup is unloaded when the confined arena - * is {@linkplain Arena#close()}: + * is {@linkplain Arena#close() closed}: * * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { @@ -210,15 +210,16 @@ public interface SymbolLookup { * For instance, if the provided arena is a confined arena, the library * associated with the returned lookup will be unloaded when the provided confined arena is * {@linkplain Arena#close() closed}. - * @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS, - * the library name is resolved according to the specification of the {@code dlopen} function for that OS. - * In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function. *

    * 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 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. + * * @param name the name of the library in which symbols should be looked up. * @param arena the arena associated with symbols obtained from the returned lookup. * @return a new symbol lookup suitable to find symbols in a library with the given name. 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 88163d61054..24f5b641991 100644 --- a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java @@ -42,9 +42,11 @@ import jdk.internal.javac.PreviewFeature; * {@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}. + * @apiNote Some characteristics of the Java layout constants are platform-dependent. For instance, the byte order of + * these constants is set to the {@linkplain ByteOrder#nativeOrder() native byte order}, thus making it easy to work + * with other APIs, such as arrays and {@link java.nio.ByteBuffer}. Moreover, the alignment constraint of + * {@link ValueLayout#JAVA_LONG} and {@link ValueLayout#JAVA_DOUBLE} is set to 8 bytes on 64-bit platforms, but only to + * 4 bytes on 32-bit platforms. * * @implSpec implementing classes and subclasses are immutable, thread-safe and value-based. * @@ -62,11 +64,9 @@ public sealed interface ValueLayout extends MemoryLayout permits ByteOrder order(); /** - * Returns a value layout with the same carrier, alignment constraint and name as this value layout, - * but with the specified byte order. + * {@return a value layout with the same characteristics as this layout, but with the given byte order} * * @param order the desired byte order. - * @return a value layout with the given byte order. */ ValueLayout withOrder(ByteOrder order); @@ -78,12 +78,10 @@ public sealed interface ValueLayout extends MemoryLayout permits /** * 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. - *

    - * The resulting var handle will feature {@code sizes.length + 1} coordinates of type {@code long}, which are - * used as indices into a multi-dimensional array. + * array. This array has a notional sequence layout featuring {@code shape.length} nested sequence layouts. The element + * layout of the innermost sequence layout in the notional sequence layout is this value layout. The resulting var handle + * is obtained as if calling the {@link #varHandle(PathElement...)} method on the notional layout, with a layout + * path containing exactly {@code shape.length + 1} {@linkplain PathElement#sequenceElement() open sequence layout path elements}. *

    * For instance, the following method call: * @@ -91,12 +89,14 @@ public sealed interface ValueLayout extends MemoryLayout permits * VarHandle arrayHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(10, 20); * } * - * Can be used to access a multi-dimensional array whose layout is as follows: + * Is equivalent to the following code: * * {@snippet lang = java: - * SequenceLayout arrayLayout = MemoryLayout.sequenceLayout( - * MemoryLayout.sequenceLayout(10, - * MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT))); + * SequenceLayout notionalLayout = MemoryLayout.sequenceLayout( + * MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT))); + * VarHandle arrayHandle = notionalLayout.varHandle(PathElement.sequenceElement(), + * PathElement.sequenceElement(), + * PathElement.sequenceElement()); *} * * The resulting var handle {@code arrayHandle} will feature 3 coordinates of type {@code long}; each coordinate @@ -110,7 +110,7 @@ public sealed interface ValueLayout extends MemoryLayout permits * * Additionally, the values of {@code x}, {@code y} and {@code z} are constrained as follows: *

      - *
    • {@code 0 <= x < arrayLayout.elementCount() }
    • + *
    • {@code 0 <= x < notionalLayout.elementCount() }
    • *
    • {@code 0 <= y < 10 }
    • *
    • {@code 0 <= z < 20 }
    • *
    @@ -448,7 +448,7 @@ public sealed interface ValueLayout extends MemoryLayout permits } /** - * A value layout constant whose size is the same as that of a machine address ({@code size_t}), + * An address layout constant whose size is the same as that of a machine address ({@code size_t}), * byte alignment set to {@code sizeof(size_t)}, byte order set to {@link ByteOrder#nativeOrder()}. */ AddressLayout ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder()); @@ -504,7 +504,7 @@ public sealed interface ValueLayout extends MemoryLayout permits 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}), + * An unaligned address 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 : 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 c905be4c249..95485943a19 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 @@ -77,8 +77,8 @@ *

    Foreign function access

    * The key abstractions introduced to support foreign function access are {@link java.lang.foreign.SymbolLookup}, * {@link java.lang.foreign.FunctionDescriptor} and {@link java.lang.foreign.Linker}. The first is used to look up symbols - * inside libraries; the second is used to model the signature of foreign functions, while the third provides - * linking capabilities which allows modelling foreign functions as {@link java.lang.invoke.MethodHandle} instances, + * inside libraries; the second is used to model the signature of foreign functions, while the third is used + * to link foreign functions as {@link java.lang.invoke.MethodHandle} instances, * so that clients can perform foreign function calls directly in Java, without the need for intermediate layers of C/C++ * code (as is the case with the Java Native Interface (JNI)). *

    @@ -116,7 +116,7 @@ *

    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#reinterpret(long)} ()} + * the restricted method {@link java.lang.foreign.MemorySegment#reinterpret(long)} * can be used to create a fresh segment with the same address and temporal bounds, * but with the provided size. This can be useful to resize memory segments obtained when interacting with native functions. *

    diff --git a/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java b/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java index b2143fe3c55..c1e18f27c02 100644 --- a/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java +++ b/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java @@ -186,16 +186,16 @@ class Snippets { } } - FunctionDescriptor compareDesc = FunctionDescriptor.of(JAVA_INT, + FunctionDescriptor comparDesc = FunctionDescriptor.of(JAVA_INT, ADDRESS.withTargetLayout(JAVA_INT), ADDRESS.withTargetLayout(JAVA_INT)); - MethodHandle compareHandle = MethodHandles.lookup() + MethodHandle comparHandle = MethodHandles.lookup() .findStatic(Qsort.class, "qsortCompare", - compareDesc.toMethodType()); + comparDesc.toMethodType()); try (Arena arena = Arena.ofConfined()) { - MemorySegment compareFunc = linker.upcallStub(compareHandle, compareDesc, arena); + MemorySegment compareFunc = linker.upcallStub(comparHandle, comparDesc, arena); MemorySegment array = arena.allocateArray(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7); qsort.invokeExact(array, 10L, 4L, compareFunc); int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] @@ -400,7 +400,7 @@ class Snippets { .findStatic(Math.class, "multiplyExact", MethodType.methodType(long.class, long.class, long.class)); intHandle = MethodHandles.filterCoordinates(intHandle, 1, - MethodHandles.insertArguments(multiplyExact, 0, 4L)); + MethodHandles.insertArguments(multiplyExact, 0, ValueLayout.JAVA_INT.byteSize())); int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 } 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 3b2313228b1..7d86f747827 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -7956,18 +7956,18 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); /** * Creates a var handle object, which can be used to dereference a {@linkplain java.lang.foreign.MemorySegment memory segment} - * by viewing its contents as a sequence of the provided value layout. + * at a given byte offset, using the provided value layout. * *

    The provided layout specifies the {@linkplain ValueLayout#carrier() carrier type}, * the {@linkplain ValueLayout#byteSize() byte size}, * the {@linkplain ValueLayout#byteAlignment() byte alignment} and the {@linkplain ValueLayout#order() byte order} * associated with the returned var handle. * - *

    The returned var handle's type is {@code carrier} and the list of coordinate types is - * {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into - * a given memory segment. The returned var handle accesses bytes at an offset in a given - * memory segment, composing bytes to or from a value of the type {@code carrier} according to the given endianness; - * the alignment constraint (in bytes) for the resulting var handle is given by {@code alignmentBytes}. + *

    The list of coordinate types associated with the returned var handle is {@code (MemorySegment, long)}, + * where the {@code long} coordinate type corresponds to byte offset into the given memory segment coordinate. + * Thus, the returned var handle accesses bytes at an offset in a given memory segment, composing bytes to or from + * a value of the var handle type. Moreover, the access operation will honor the endianness and the + * alignment constraints expressed in the provided layout. * *

    As an example, consider the memory layout expressed by a {@link GroupLayout} instance constructed as follows: * {@snippet lang="java" : @@ -8023,7 +8023,6 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); * * @param layout the value layout for which a memory access handle is to be obtained. * @return the new memory segment view var handle. - * @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two. * @throws NullPointerException if {@code layout} is {@code null}. * @see MemoryLayout#varHandle(MemoryLayout.PathElement...) * @since 19 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 54042c842b7..ad533280388 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -114,6 +114,8 @@ public abstract sealed class AbstractMemorySegmentImpl @Override public MemorySegment asSlice(long offset, long newSize, long byteAlignment) { checkBounds(offset, newSize); + Utils.checkAlign(byteAlignment); + if (!isAlignedForElement(offset, byteAlignment)) { throw new IllegalArgumentException("Target offset incompatible with alignment constraints"); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/Utils.java b/src/java.base/share/classes/jdk/internal/foreign/Utils.java index bb990540942..ca1a6941530 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Utils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/Utils.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import jdk.internal.access.SharedSecrets; import jdk.internal.foreign.abi.SharedUtils; @@ -166,11 +167,16 @@ public final class Utils { } @ForceInline - public static void checkElementAlignment(ValueLayout layout, String msg) { + public static boolean isElementAligned(ValueLayout layout) { // Fast-path: if both size and alignment are powers of two, we can just // check if one is greater than the other. assert isPowerOfTwo(layout.byteSize()); - if (layout.byteAlignment() > layout.byteSize()) { + return layout.byteAlignment() <= layout.byteSize(); + } + + @ForceInline + public static void checkElementAlignment(ValueLayout layout, String msg) { + if (!isElementAligned(layout)) { throw new IllegalArgumentException(msg); } } @@ -200,6 +206,10 @@ public final class Utils { throw new IllegalArgumentException("Invalid allocation size : " + byteSize); } + checkAlign(byteAlignment); + } + + public static void checkAlign(long byteAlignment) { // alignment should be > 0, and power of two if (byteAlignment <= 0 || ((byteAlignment & (byteAlignment - 1)) != 0L)) { @@ -252,6 +262,14 @@ public final class Utils { return (value & (value - 1)) == 0L; } + public static L wrapOverflow(Supplier layoutSupplier) { + try { + return layoutSupplier.get(); + } catch (ArithmeticException ex) { + throw new IllegalArgumentException("Layout size exceeds Long.MAX_VALUE"); + } + } + public static boolean containsNullChars(String s) { return s.indexOf('\u0000') >= 0; } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index dea56564c6b..b5eb1029ff5 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -55,6 +55,7 @@ import java.lang.invoke.MethodType; import java.util.List; import java.nio.ByteOrder; import java.util.Objects; +import java.util.Set; public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker, SysVx64Linker, WindowsAArch64Linker, @@ -173,10 +174,10 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch } private void checkLayoutRecursive(MemoryLayout layout) { - checkHasNaturalAlignment(layout); if (layout instanceof ValueLayout vl) { - checkByteOrder(vl); + checkSupported(vl); } else if (layout instanceof StructLayout sl) { + checkHasNaturalAlignment(layout); long offset = 0; long lastUnpaddedOffset = 0; for (MemoryLayout member : sl.memberLayouts()) { @@ -192,6 +193,7 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch } checkGroupSize(sl, lastUnpaddedOffset); } else if (layout instanceof UnionLayout ul) { + checkHasNaturalAlignment(layout); long maxUnpaddedLayout = 0; for (MemoryLayout member : ul.memberLayouts()) { checkLayoutRecursive(member); @@ -201,6 +203,7 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch } checkGroupSize(ul, maxUnpaddedLayout); } else if (layout instanceof SequenceLayout sl) { + checkHasNaturalAlignment(layout); checkLayoutRecursive(sl.elementLayout()); } } @@ -225,6 +228,16 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch } } + private static void checkSupported(ValueLayout valueLayout) { + valueLayout = valueLayout.withoutName(); + if (valueLayout instanceof AddressLayout addressLayout) { + valueLayout = addressLayout.withoutTargetLayout(); + } + if (!SUPPORTED_LAYOUTS.contains(valueLayout.withoutName())) { + throw new IllegalArgumentException("Unsupported layout: " + valueLayout); + } + } + private static void checkHasNaturalAlignment(MemoryLayout layout) { if (!((AbstractLayout) layout).hasNaturalAlignment()) { throw new IllegalArgumentException("Layout alignment must be natural alignment: " + layout); @@ -257,9 +270,15 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch .orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts()))); } - private void checkByteOrder(ValueLayout vl) { - if (vl.order() != linkerByteOrder()) { - throw new IllegalArgumentException("Layout does not have the right byte order: " + vl); - } - } + private static final Set SUPPORTED_LAYOUTS = Set.of( + ValueLayout.JAVA_BOOLEAN, + ValueLayout.JAVA_BYTE, + ValueLayout.JAVA_CHAR, + ValueLayout.JAVA_SHORT, + ValueLayout.JAVA_INT, + ValueLayout.JAVA_FLOAT, + ValueLayout.JAVA_LONG, + ValueLayout.JAVA_DOUBLE, + ValueLayout.ADDRESS + ); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java index 12aa063153b..bcbf76ea338 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java @@ -25,6 +25,8 @@ */ package jdk.internal.foreign.layout; +import jdk.internal.foreign.Utils; + import java.lang.foreign.MemoryLayout; import java.lang.foreign.SequenceLayout; import java.util.Objects; @@ -68,7 +70,8 @@ public final class SequenceLayoutImpl extends AbstractLayout * @throws IllegalArgumentException if {@code elementCount < 0}. */ public SequenceLayout withElementCount(long elementCount) { - return new SequenceLayoutImpl(elementCount, elementLayout, byteAlignment(), name()); + return Utils.wrapOverflow(() -> + new SequenceLayoutImpl(elementCount, elementLayout, byteAlignment(), name())); } /** diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java index 57016f548c4..807da9cb253 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java @@ -117,6 +117,9 @@ public final class ValueLayouts { public final VarHandle arrayElementVarHandle(int... shape) { Objects.requireNonNull(shape); + if (!Utils.isElementAligned((ValueLayout) this)) { + throw new UnsupportedOperationException("Layout alignment greater than its size"); + } MemoryLayout layout = self(); List path = new ArrayList<>(); for (int i = shape.length; i > 0; i--) { diff --git a/test/jdk/java/foreign/TestArrayCopy.java b/test/jdk/java/foreign/TestArrayCopy.java index 2a2b740cd91..a2302249d3a 100644 --- a/test/jdk/java/foreign/TestArrayCopy.java +++ b/test/jdk/java/foreign/TestArrayCopy.java @@ -227,6 +227,21 @@ public class TestArrayCopy { } } + @Test(dataProvider = "copyModesAndHelpers") + public void testCopyReadOnlyDest(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + //CopyFrom + Object srcArr = helper.toArray(base); + MemorySegment dstSeg = helper.fromArray(srcArr).asReadOnly(); + try { + helper.copyFromArray(srcArr, 0, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, 0, ByteOrder.nativeOrder()); + fail(); + } catch (UnsupportedOperationException ex) { + //ok + } + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testNotAnArraySrc() { MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4}); diff --git a/test/jdk/java/foreign/TestDereferencePath.java b/test/jdk/java/foreign/TestDereferencePath.java index 4e50ffca00f..7962322081e 100644 --- a/test/jdk/java/foreign/TestDereferencePath.java +++ b/test/jdk/java/foreign/TestDereferencePath.java @@ -130,6 +130,11 @@ public class TestDereferencePath { A.byteOffset(PathElement.groupElement("b"), PathElement.dereferenceElement()); } + @Test(expectedExceptions = IllegalArgumentException.class) + void testBadDerefInSlice() { + A.sliceHandle(PathElement.groupElement("b"), PathElement.dereferenceElement()); + } + static final MemoryLayout A_MULTI_NO_TARGET = MemoryLayout.structLayout( ValueLayout.ADDRESS.withName("bs") ); diff --git a/test/jdk/java/foreign/TestIllegalLink.java b/test/jdk/java/foreign/TestIllegalLink.java index 9360e6f8559..677f0bce62f 100644 --- a/test/jdk/java/foreign/TestIllegalLink.java +++ b/test/jdk/java/foreign/TestIllegalLink.java @@ -123,17 +123,17 @@ public class TestIllegalLink extends NativeTestHelper { { FunctionDescriptor.ofVoid(C_INT.withByteAlignment(2)), NO_OPTIONS, - "Layout alignment must be natural alignment" + "Unsupported layout: 2%i4" }, { FunctionDescriptor.ofVoid(C_POINTER.withByteAlignment(2)), NO_OPTIONS, - "Layout alignment must be natural alignment" + "Unsupported layout: 2%a8" }, { FunctionDescriptor.ofVoid(ValueLayout.JAVA_CHAR.withByteAlignment(4)), NO_OPTIONS, - "Layout alignment must be natural alignment" + "Unsupported layout: 4%c2" }, { FunctionDescriptor.ofVoid(MemoryLayout.structLayout( @@ -142,7 +142,7 @@ public class TestIllegalLink extends NativeTestHelper { C_INT.withName("z").withByteAlignment(1) ).withByteAlignment(1)), NO_OPTIONS, - "Layout alignment must be natural alignment" + "Unsupported layout: 1%s2" }, { FunctionDescriptor.ofVoid(MemoryLayout.structLayout( @@ -152,7 +152,7 @@ public class TestIllegalLink extends NativeTestHelper { C_INT.withName("z").withByteAlignment(1) ))), NO_OPTIONS, - "Layout alignment must be natural alignment" + "Unsupported layout: 1%s2" }, { FunctionDescriptor.ofVoid(MemoryLayout.structLayout( @@ -160,7 +160,7 @@ public class TestIllegalLink extends NativeTestHelper { C_INT.withByteAlignment(1) ))), NO_OPTIONS, - "Layout alignment must be natural alignment" + "Unsupported layout: 1%i4" }, { FunctionDescriptor.ofVoid(MemoryLayout.structLayout( @@ -173,17 +173,17 @@ public class TestIllegalLink extends NativeTestHelper { { FunctionDescriptor.of(C_INT.withOrder(nonNativeOrder())), NO_OPTIONS, - "Layout does not have the right byte order" + "Unsupported layout: I4" }, { FunctionDescriptor.of(MemoryLayout.structLayout(C_INT.withOrder(nonNativeOrder()))), NO_OPTIONS, - "Layout does not have the right byte order" + "Unsupported layout: I4" }, { FunctionDescriptor.of(MemoryLayout.structLayout(MemoryLayout.sequenceLayout(C_INT.withOrder(nonNativeOrder())))), NO_OPTIONS, - "Layout does not have the right byte order" + "Unsupported layout: I4" }, { FunctionDescriptor.ofVoid(MemoryLayout.structLayout( diff --git a/test/jdk/java/foreign/TestLayoutPaths.java b/test/jdk/java/foreign/TestLayoutPaths.java index bfa1cedb3fa..ef6a68f5701 100644 --- a/test/jdk/java/foreign/TestLayoutPaths.java +++ b/test/jdk/java/foreign/TestLayoutPaths.java @@ -122,10 +122,16 @@ public class TestLayoutPaths { seq.varHandle(sequenceElement()); } + @Test + public void testByteOffsetHandleRange() { + SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT)); + seq.byteOffsetHandle(sequenceElement(0, 1)); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testByteOffsetHandleBadRange() { SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT)); - seq.byteOffsetHandle(sequenceElement(0, 1)); // ranges not accepted + seq.byteOffsetHandle(sequenceElement(5, 1)); // invalid range (starting position is outside the sequence) } @Test diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java index ebb7bcf0585..af1a8df6d49 100644 --- a/test/jdk/java/foreign/TestLayouts.java +++ b/test/jdk/java/foreign/TestLayouts.java @@ -218,6 +218,8 @@ public class TestLayouts { () -> MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_SHORT)); assertThrows(IllegalArgumentException.class, // flip back to positive () -> MemoryLayout.sequenceLayout(Long.MAX_VALUE/3, JAVA_LONG)); + assertThrows(IllegalArgumentException.class, // flip back to positive + () -> MemoryLayout.sequenceLayout(0, JAVA_LONG).withElementCount(Long.MAX_VALUE)); } @Test @@ -333,6 +335,14 @@ public class TestLayouts { } } + @Test(dataProvider="layoutsAndAlignments") + public void testArrayElementVarHandleBadAlignment(MemoryLayout layout, long byteAlign) { + if (layout instanceof ValueLayout) { + assertThrows(UnsupportedOperationException.class, () -> + ((ValueLayout) layout).withByteAlignment(byteAlign * 2).arrayElementVarHandle()); + } + } + @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class) public void testBadStruct(MemoryLayout layout, long byteAlign) { layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align diff --git a/test/jdk/java/foreign/TestMemoryAccessInstance.java b/test/jdk/java/foreign/TestMemoryAccessInstance.java index c8e1fda6da1..73a2df898e4 100644 --- a/test/jdk/java/foreign/TestMemoryAccessInstance.java +++ b/test/jdk/java/foreign/TestMemoryAccessInstance.java @@ -27,12 +27,12 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance */ +import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.function.Function; import org.testng.annotations.*; import org.testng.SkipException; @@ -40,14 +40,14 @@ import static org.testng.Assert.*; public class TestMemoryAccessInstance { - static class Accessor { + static class Accessor { - interface SegmentGetter { - X get(T buffer, L layout, long offset); + interface SegmentGetter { + X get(MemorySegment segment, L layout, long offset); } - interface SegmentSetter { - void set(T buffer, L layout, long offset, X o); + interface SegmentSetter { + void set(MemorySegment segment, L layout, long offset, X o); } interface BufferGetter { @@ -60,16 +60,14 @@ public class TestMemoryAccessInstance { final X value; final L layout; - final Function transform; - final SegmentGetter segmentGetter; - final SegmentSetter segmentSetter; + final SegmentGetter segmentGetter; + final SegmentSetter segmentSetter; final BufferGetter bufferGetter; final BufferSetter bufferSetter; - Accessor(Function transform, L layout, X value, - SegmentGetter segmentGetter, SegmentSetter segmentSetter, + Accessor(L layout, X value, + SegmentGetter segmentGetter, SegmentSetter segmentSetter, BufferGetter bufferGetter, BufferSetter bufferSetter) { - this.transform = transform; this.layout = layout; this.value = value; this.segmentGetter = segmentGetter; @@ -82,11 +80,10 @@ public class TestMemoryAccessInstance { try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(128, 1); ByteBuffer buffer = segment.asByteBuffer(); - T t = transform.apply(segment); - segmentSetter.set(t, layout, 8, value); + segmentSetter.set(segment, layout, 8, value); assertEquals(bufferGetter.get(buffer, 8), value); bufferSetter.set(buffer, 8, value); - assertEquals(value, segmentGetter.get(t, layout, 8)); + assertEquals(value, segmentGetter.get(segment, layout, 8)); } } @@ -94,16 +91,15 @@ public class TestMemoryAccessInstance { void testHyperAligned() { try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(64, 1); - T t = transform.apply(segment); L alignedLayout = (L)layout.withByteAlignment(layout.byteSize() * 2); try { - segmentSetter.set(t, alignedLayout, 0, value); + segmentSetter.set(segment, alignedLayout, 0, value); fail(); } catch (IllegalArgumentException exception) { assertTrue(exception.getMessage().contains("greater")); } try { - segmentGetter.get(t, alignedLayout, 0); + segmentGetter.get(segment, alignedLayout, 0); fail(); } catch (IllegalArgumentException exception) { assertTrue(exception.getMessage().contains("greater")); @@ -111,20 +107,28 @@ public class TestMemoryAccessInstance { } } - static Accessor ofSegment(L layout, X value, - SegmentGetter segmentGetter, SegmentSetter segmentSetter, - BufferGetter bufferGetter, BufferSetter bufferSetter) { - return new Accessor<>(Function.identity(), layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); + X get(MemorySegment segment, long offset) { + return segmentGetter.get(segment, layout, offset); + } + + void set(MemorySegment segment, long offset, X value) { + segmentSetter.set(segment, layout, offset, value); + } + + static Accessor of(L layout, X value, + SegmentGetter segmentGetter, SegmentSetter segmentSetter, + BufferGetter bufferGetter, BufferSetter bufferSetter) { + return new Accessor<>(layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); } } @Test(dataProvider = "segmentAccessors") - public void testSegmentAccess(String testName, Accessor accessor) { + public void testSegmentAccess(String testName, Accessor accessor) { accessor.test(); } @Test(dataProvider = "segmentAccessors") - public void testSegmentAccessHyper(String testName, Accessor accessor) { + public void testSegmentAccessHyper(String testName, Accessor accessor) { if (testName.contains("index")) { accessor.testHyperAligned(); } else { @@ -152,45 +156,54 @@ public class TestMemoryAccessInstance { targetSegment.setAtIndex(ValueLayout.ADDRESS, 0, segment); // should throw } + @Test(dataProvider = "segmentAccessors") + public void badAccessOverflowInIndexedAccess(String testName, Accessor accessor) { + MemorySegment segment = MemorySegment.ofArray(new byte[100]); + if (testName.contains("/index") && accessor.layout.byteSize() > 1) { + assertThrows(IndexOutOfBoundsException.class, () -> accessor.get(segment, Long.MAX_VALUE)); + assertThrows(IndexOutOfBoundsException.class, () -> accessor.set(segment, Long.MAX_VALUE, accessor.value)); + } + } + static final ByteOrder NE = ByteOrder.nativeOrder(); @DataProvider(name = "segmentAccessors") static Object[][] segmentAccessors() { return new Object[][]{ - {"byte", Accessor.ofSegment(ValueLayout.JAVA_BYTE, (byte) 42, + {"byte", Accessor.of(ValueLayout.JAVA_BYTE, (byte) 42, MemorySegment::get, MemorySegment::set, ByteBuffer::get, ByteBuffer::put) }, - {"boolean", Accessor.ofSegment(ValueLayout.JAVA_BOOLEAN, false, + {"boolean", Accessor.of(ValueLayout.JAVA_BOOLEAN, false, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0)) }, - {"char", Accessor.ofSegment(ValueLayout.JAVA_CHAR, (char) 42, + {"char", Accessor.of(ValueLayout.JAVA_CHAR, (char) 42, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v)) }, - {"short", Accessor.ofSegment(ValueLayout.JAVA_SHORT, (short) 42, + {"short", Accessor.of(ValueLayout.JAVA_SHORT, (short) 42, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.order(NE).getShort(pos), (bb, pos, v) -> bb.order(NE).putShort(pos, v)) }, - {"int", Accessor.ofSegment(ValueLayout.JAVA_INT, 42, + {"int", Accessor.of(ValueLayout.JAVA_INT, 42, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v)) }, - {"float", Accessor.ofSegment(ValueLayout.JAVA_FLOAT, 42f, + {"float", Accessor.of(ValueLayout.JAVA_FLOAT, 42f, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v)) }, - {"long", Accessor.ofSegment(ValueLayout.JAVA_LONG, 42L, + {"long", Accessor.of(ValueLayout.JAVA_LONG, 42L, MemorySegment::get, MemorySegment::set, (bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v)) }, - {"double", Accessor.ofSegment(ValueLayout.JAVA_DOUBLE, 42d, + {"double", Accessor.of(ValueLayout.JAVA_DOUBLE, 42d, 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, MemorySegment.ofAddress(42), + { "address", Accessor.of(ValueLayout.ADDRESS, MemorySegment.ofAddress(42), MemorySegment::get, MemorySegment::set, (bb, pos) -> { ByteBuffer nb = bb.order(NE); @@ -208,39 +221,39 @@ public class TestMemoryAccessInstance { }) }, - {"byte/index", Accessor.ofSegment(ValueLayout.JAVA_BYTE, (byte) 42, + {"byte/index", Accessor.of(ValueLayout.JAVA_BYTE, (byte) 42, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).get(pos), (bb, pos, v) -> bb.order(NE).put(pos, v)) }, - {"boolean/index", Accessor.ofSegment(ValueLayout.JAVA_BOOLEAN, true, + {"boolean/index", Accessor.of(ValueLayout.JAVA_BOOLEAN, true, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).get(pos) != 0, (bb, pos, v) -> bb.order(NE).put(pos, (byte) (v ? 1 : 0))) }, - {"char/index", Accessor.ofSegment(ValueLayout.JAVA_CHAR, (char) 42, + {"char/index", Accessor.of(ValueLayout.JAVA_CHAR, (char) 42, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v)) }, - {"short/index", Accessor.ofSegment(ValueLayout.JAVA_SHORT, (short) 42, + {"short/index", Accessor.of(ValueLayout.JAVA_SHORT, (short) 42, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).getShort(pos * 2), (bb, pos, v) -> bb.order(NE).putShort(pos * 2, v)) }, - {"int/index", Accessor.ofSegment(ValueLayout.JAVA_INT, 42, + {"int/index", Accessor.of(ValueLayout.JAVA_INT, 42, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v)) }, - {"float/index", Accessor.ofSegment(ValueLayout.JAVA_FLOAT, 42f, + {"float/index", Accessor.of(ValueLayout.JAVA_FLOAT, 42f, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v)) }, - {"long/index", Accessor.ofSegment(ValueLayout.JAVA_LONG, 42L, + {"long/index", Accessor.of(ValueLayout.JAVA_LONG, 42L, MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v)) }, - {"double/index", Accessor.ofSegment(ValueLayout.JAVA_DOUBLE, 42d, + {"double/index", Accessor.of(ValueLayout.JAVA_DOUBLE, 42d, 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, MemorySegment.ofAddress(42), + { "address/index", Accessor.of(ValueLayout.ADDRESS, MemorySegment.ofAddress(42), MemorySegment::getAtIndex, MemorySegment::setAtIndex, (bb, pos) -> { ByteBuffer nb = bb.order(NE); diff --git a/test/jdk/java/foreign/TestSegmentAllocators.java b/test/jdk/java/foreign/TestSegmentAllocators.java index c52c7756245..4c574a60500 100644 --- a/test/jdk/java/foreign/TestSegmentAllocators.java +++ b/test/jdk/java/foreign/TestSegmentAllocators.java @@ -151,6 +151,11 @@ public class TestSegmentAllocators { allocator.allocateArray(ValueLayout.JAVA_BYTE, -1); } + @Test(dataProvider = "allocators", expectedExceptions = IllegalArgumentException.class) + public void testBadAllocationArrayOverflow(SegmentAllocator allocator) { + allocator.allocateArray(ValueLayout.JAVA_LONG, Long.MAX_VALUE); + } + @Test(expectedExceptions = OutOfMemoryError.class) public void testBadArenaNullReturn() { try (Arena arena = Arena.ofConfined()) { diff --git a/test/jdk/java/foreign/TestSegmentCopy.java b/test/jdk/java/foreign/TestSegmentCopy.java index 504e81d5528..cf118658271 100644 --- a/test/jdk/java/foreign/TestSegmentCopy.java +++ b/test/jdk/java/foreign/TestSegmentCopy.java @@ -35,9 +35,11 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.IntFunction; +import org.testng.SkipException; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -75,6 +77,24 @@ public class TestSegmentCopy { } } + @Test(expectedExceptions = UnsupportedOperationException.class, dataProvider = "segmentKinds") + public void testReadOnlyCopy(SegmentKind kind1, SegmentKind kind2) { + MemorySegment s1 = kind1.makeSegment(TEST_BYTE_SIZE); + MemorySegment s2 = kind2.makeSegment(TEST_BYTE_SIZE); + // check failure with read-only dest + MemorySegment.copy(s1, Type.BYTE.layout, 0, s2.asReadOnly(), Type.BYTE.layout, 0, 0); + } + + @Test(expectedExceptions = IndexOutOfBoundsException.class, dataProvider = "types") + public void testBadOverflow(Type type) { + if (type.layout.byteSize() > 1) { + MemorySegment segment = MemorySegment.ofArray(new byte[100]); + MemorySegment.copy(segment, type.layout, 0, segment, type.layout, 0, Long.MAX_VALUE); + } else { + throw new SkipException("Byte layouts do not overflow"); + } + } + @Test(dataProvider = "segmentKindsAndTypes") public void testElementCopy(SegmentKind kind1, SegmentKind kind2, Type type1, Type type2) { MemorySegment s1 = kind1.makeSegment(TEST_BYTE_SIZE); @@ -189,6 +209,13 @@ public class TestSegmentCopy { return cases.toArray(Object[][]::new); } + @DataProvider + static Object[][] types() { + return Arrays.stream(Type.values()) + .map(t -> new Object[] { t }) + .toArray(Object[][]::new); + } + @DataProvider static Object[][] segmentKindsAndTypes() { List cases = new ArrayList<>(); diff --git a/test/jdk/java/foreign/TestSlices.java b/test/jdk/java/foreign/TestSlices.java index 988d4423b2c..fb8c6dc95dd 100644 --- a/test/jdk/java/foreign/TestSlices.java +++ b/test/jdk/java/foreign/TestSlices.java @@ -134,6 +134,22 @@ public class TestSlices { } } + @Test + public void testSliceAlignmentPowerOfTwo() { + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(100, 4096); + for (int i = 8 ; i < 4096 ; i++) { + boolean badAlign = Long.bitCount(i) != 1; // not a power of two + try { + segment.asSlice(0, 100, i); + assertFalse(badAlign); + } catch (IllegalArgumentException iae) { + assertTrue(badAlign); + } + } + } + } + @DataProvider(name = "slices") static Object[][] slices() { return new Object[][] {