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 acd3546f335f2..eccbf5fa92980 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -571,6 +571,10 @@ public sealed interface MemoryLayout * derived from the size of the element layout of a sequence, and * {@code c_1}, {@code c_2}, ... {@code c_m} are other static offset * constants (such as field offsets) which are derived from the layout path. + *

+ * The returned method handle throws {@link IndexOutOfBoundsException} if the provided layout path has an open + * path element whose size is {@code S}, and its corresponding trailing {@code long} parameter + * has a value {@code I} such that {@code I >= S} or {@code I < 0}. * * @apiNote The returned method handle can be used to compute a layout offset, * similarly to {@link #byteOffset(PathElement...)}, but more flexibly, as @@ -609,7 +613,7 @@ public sealed interface MemoryLayout * offset {@code O} of the access operation is computed as follows: * * {@snippet lang = "java": - * O = this.offsetHandle(P).invokeExact(B, I1, I2, ... In); + * O = this.byteOffsetHandle(P).invokeExact(B, I1, I2, ... In); * } *

* Accessing a memory segment using the var handle returned by this method is subject @@ -728,7 +732,7 @@ public sealed interface MemoryLayout * offset {@code O} of the access operation is computed as follows: * * {@snippet lang = "java": - * O = this.offsetHandle(P).invokeExact(this.scale(B, I0), I1, I2, ... In); + * O = this.byteOffsetHandle(P).invokeExact(this.scale(B, I0), I1, I2, ... In); * } *

* More formally, this method can be obtained from the {@link #varHandle(PathElement...)}, @@ -838,7 +842,8 @@ public sealed interface MemoryLayout * layout as its target layout. * * Sequence path elements selecting more than one sequence element layout are called - * open path elements. + * open path elements. The size + * of an open path element determines the number of element layouts that can be selected by it. * * @implSpec * Implementations of this interface are immutable, thread-safe and @@ -906,6 +911,7 @@ static PathElement sequenceElement(long index) { *

  • if {@code F > 0}, then {@code B = ceilDiv(C - S, F)}
  • *
  • if {@code F < 0}, then {@code B = ceilDiv(-(S + 1), -F)}
  • * + * That is, the size of the returned open path element is {@code B}. * * @param start the index of the first sequence element to be selected * @param step the step factor at which subsequence sequence elements are to be @@ -924,7 +930,7 @@ static PathElement sequenceElement(long start, long step) { *

    * 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}. + * {@code 0 <= I < C}. That is, {@code C} is the size of the returned open path element. */ static PathElement sequenceElement() { return LayoutPath.SequenceElement.instance(); diff --git a/test/jdk/java/foreign/TestLayoutPaths.java b/test/jdk/java/foreign/TestLayoutPaths.java index e6bfe8450a0e8..6a85252152db3 100644 --- a/test/jdk/java/foreign/TestLayoutPaths.java +++ b/test/jdk/java/foreign/TestLayoutPaths.java @@ -36,8 +36,10 @@ 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 java.util.stream.Stream; import static java.lang.foreign.MemoryLayout.PathElement.groupElement; import static java.lang.foreign.MemoryLayout.PathElement.sequenceElement; @@ -311,6 +313,24 @@ public void testOffsetHandle(MemoryLayout layout, PathElement[] pathElements, lo assertEquals(actualByteOffset, expectedByteOffset); } + @Test(dataProvider = "testLayouts") + public void testOffsetHandleOOBIndex(MemoryLayout layout, PathElement[] pathElements, long[] indexes, + long expectedByteOffset) throws Throwable { + int[] badIndices = { -1, 10 }; + MemoryLayout seqLayout = MemoryLayout.sequenceLayout(badIndices[1], layout); + for (int badIndex : badIndices) { + PathElement[] seqPathElements = new PathElement[pathElements.length + 1]; + long[] seqIndexes = new long[indexes.length + 1]; + System.arraycopy(pathElements, 0, seqPathElements, 1, pathElements.length); + System.arraycopy(indexes, 0, seqIndexes, 1, indexes.length); + seqPathElements[0] = PathElement.sequenceElement(); + seqIndexes[0] = badIndex; + MethodHandle seqByteOffsetHandle = seqLayout.byteOffsetHandle(seqPathElements) + .asSpreader(long[].class, seqIndexes.length); + assertThrows(IndexOutOfBoundsException.class, () -> seqByteOffsetHandle.invoke(0L, seqIndexes)); + } + } + @Test public void testHashCodeCollision() { PathElement sequenceElement = PathElement.sequenceElement();