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 d7636032c2823..1cc1c54041eb0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -68,7 +68,7 @@ * {@link MappedMemorySegmentImpl}. */ public abstract sealed class AbstractMemorySegmentImpl - implements MemorySegment, SegmentAllocator, BiFunction, RuntimeException> + implements MemorySegment, SegmentAllocator permits HeapMemorySegmentImpl, NativeMemorySegmentImpl { static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess(); @@ -100,19 +100,19 @@ public boolean isReadOnly() { @Override public AbstractMemorySegmentImpl asSlice(long offset, long newSize) { - checkBounds(offset, newSize); + checkSliceBounds(offset, newSize); return asSliceNoCheck(offset, newSize); } @Override public AbstractMemorySegmentImpl asSlice(long offset) { - checkBounds(offset, 0); + checkSliceBounds(offset, 0); return asSliceNoCheck(offset, length - offset); } @Override public MemorySegment asSlice(long offset, long newSize, long byteAlignment) { - checkBounds(offset, newSize); + checkSliceBounds(offset, newSize); Utils.checkAlign(byteAlignment); if (!isAlignedForElement(offset, byteAlignment)) { @@ -354,7 +354,7 @@ public void checkReadOnly(boolean readOnly) { @ForceInline public void checkAccess(long offset, long length, boolean readOnly) { checkReadOnly(readOnly); - checkBounds(offset, length); + checkAccessBounds(offset, length); } @ForceInline @@ -398,20 +398,22 @@ private int checkArraySize(String typeName, int elemSize) { } @ForceInline - void checkBounds(long offset, long length) { - if (length > 0) { - Preconditions.checkIndex(offset, this.length - length + 1, this); - } else if (length < 0 || offset < 0 || - offset > this.length - length) { - throw outOfBoundException(offset, length); - } + void checkSliceBounds(long offset, long length) { + checkBounds(offset, length, BoundPolicy.SLICE); } - @Override - public RuntimeException apply(String s, List numbers) { - long offset = numbers.get(0).longValue(); - long length = byteSize() - numbers.get(1).longValue() + 1; - return outOfBoundException(offset, length); + @ForceInline + void checkAccessBounds(long offset, long length) { + checkBounds(offset, length, BoundPolicy.ACCESS); + } + + @ForceInline + private void checkBounds(long offset, long length, BoundPolicy policy) { + if (length > 0) { + Preconditions.checkIndex(offset, this.length - length + 1, new BoundsCheckHandler(this, policy)); + } else if (length < 0 || offset < 0 || offset > this.length - length) { + throw policy.outOfBoundException(this, offset, length, this.length); + } } @Override @@ -429,11 +431,6 @@ public final MemorySessionImpl sessionImpl() { return scope; } - private IndexOutOfBoundsException outOfBoundException(long offset, long length) { - return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d", - this, offset, length)); - } - static class SegmentSplitter implements Spliterator { AbstractMemorySegmentImpl segment; long elemCount; @@ -508,6 +505,33 @@ public int characteristics() { } } + private enum BoundPolicy { + ACCESS, SLICE; + + private IndexOutOfBoundsException outOfBoundException(AbstractMemorySegmentImpl segment, long offset, long size, + long length) { + String formatString = switch (this) { + case ACCESS -> "Out of bound access on segment %s; attempting to access an element of length %d at offset %d " + + "which is outside the valid range 0 <= offset+length < byteSize (=%d)"; + case SLICE -> "Out of bound access on segment %s; attempting to get slice of length %d at offset %d " + + "which is outside the valid range 0 <= offset+length < byteSize (=%d)"; + }; + String message = formatString.formatted(segment, size, offset, length); + return new IndexOutOfBoundsException(message); + } + } + + private record BoundsCheckHandler(AbstractMemorySegmentImpl segment, BoundPolicy policy) + implements BiFunction, IndexOutOfBoundsException> { + @Override + public IndexOutOfBoundsException apply(String s, List numbers) { + long offset = numbers.get(0).longValue(); + long length = segment.byteSize() - numbers.get(1).longValue() + 1; + + return policy.outOfBoundException(segment, offset, length, segment.byteSize()); + } + } + // Object methods @Override diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java index 5af4bc376929d..ab6b78b73eee8 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java @@ -194,7 +194,7 @@ public static void copy(AbstractMemorySegmentImpl src, long srcOffset, @ForceInline public static int contentHash(AbstractMemorySegmentImpl segment, long fromOffset, long toOffset) { final long length = toOffset - fromOffset; - segment.checkBounds(fromOffset, length); + segment.checkSliceBounds(fromOffset, length); if (length == 0) { // The state has to be checked explicitly for zero-length segments segment.scope.checkValidState(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java index bb6cb2d391544..208c6d54aab36 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java +++ b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java @@ -145,7 +145,7 @@ public static int strlenByte(final AbstractMemorySegmentImpl segment, final long fromOffset, final long toOffset) { final long length = toOffset - fromOffset; - segment.checkBounds(fromOffset, length); + segment.checkSliceBounds(fromOffset, length); if (length < Byte.BYTES) { // There can be no null terminator present segment.scope.checkValidState(); @@ -179,7 +179,7 @@ public static int strlenShort(final AbstractMemorySegmentImpl segment, final long fromOffset, final long toOffset) { final long length = toOffset - fromOffset; - segment.checkBounds(fromOffset, length); + segment.checkSliceBounds(fromOffset, length); if (length < Short.BYTES) { // There can be no null terminator present segment.scope.checkValidState(); @@ -215,7 +215,7 @@ public static int strlenInt(final AbstractMemorySegmentImpl segment, final long fromOffset, final long toOffset) { final long length = toOffset - fromOffset; - segment.checkBounds(fromOffset, length); + segment.checkSliceBounds(fromOffset, length); if (length < Integer.BYTES) { // There can be no null terminator present segment.scope.checkValidState(); diff --git a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java index 2942b388d50c6..e9f3e8a87cc21 100644 --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -212,14 +212,32 @@ public void testSmallSegmentMin() { } @Test - public void testSegmentOOBMessage() { + public void testSegmentAccessOOBMessage() { try { var segment = Arena.global().allocate(10, 1); segment.getAtIndex(ValueLayout.JAVA_INT, 2); + fail("Expected IndexOutOfBoundsException was not thrown"); } catch (IndexOutOfBoundsException ex) { - assertTrue(ex.getMessage().contains("Out of bound access")); - assertTrue(ex.getMessage().contains("offset = 8")); - assertTrue(ex.getMessage().contains("length = 4")); + assertTrue(ex.getMessage().startsWith("Out of bound access")); + assertTrue(ex.getMessage().endsWith("attempting to access an element of length 4 at offset 8 " + + "which is outside the valid range 0 <= offset+length < byteSize (=10)")); + } catch (Exception ex) { + fail("Unexpected exception type thrown: " + ex); + } + } + + @Test + public void testSegmentSliceOOBMessage() { + try { + var segment = Arena.global().allocate(10, 1); + var slice = segment.asSlice(8, 4); + fail("Expected IndexOutOfBoundsException was not thrown"); + } catch (IndexOutOfBoundsException ex) { + assertTrue(ex.getMessage().startsWith("Out of bound access")); + assertTrue(ex.getMessage().endsWith("attempting to get slice of length 4 at offset 8 " + + "which is outside the valid range 0 <= offset+length < byteSize (=10)")); + } catch (Exception ex) { + fail("Unexpected exception type thrown: " + ex); } }