From a106ac87cc12cac2de9fb96eb6f6950029c6dc6c Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Tue, 4 Nov 2025 11:16:26 +0200 Subject: [PATCH 1/9] JDK-8346657: Improve out of bounds exception messages for MemorySegments --- .../jdk/internal/foreign/AbstractMemorySegmentImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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..b3b55acfafc02 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -430,8 +430,10 @@ public final MemorySessionImpl sessionImpl() { } 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)); + return new IndexOutOfBoundsException(String.format("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)", + this, length, offset, byteSize())); } static class SegmentSplitter implements Spliterator { From adc7a06842a8ffe78b10e0539e86d9621d791d68 Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Sat, 8 Nov 2025 22:51:09 +0200 Subject: [PATCH 2/9] Fixes according to reviewer's comments --- .../foreign/AbstractMemorySegmentImpl.java | 84 ++++++++++++++----- .../foreign/SegmentBulkOperations.java | 2 +- .../jdk/internal/foreign/StringSupport.java | 6 +- test/jdk/java/foreign/TestSegments.java | 26 +++++- 4 files changed, 88 insertions(+), 30 deletions(-) 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 b3b55acfafc02..e28e5dc0111f0 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,27 @@ private int checkArraySize(String typeName, int elemSize) { } @ForceInline - void checkBounds(long offset, long length) { + private void checkBounds(long offset, long length, BoundPolicy policy) { 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); + Preconditions.checkIndex(offset, this.length - length + 1, (s, nums) -> { + long off = nums.get(0).longValue(); + long len = this.byteSize() - nums.get(1).longValue() + 1; + return policy.makeException(this, off, len); + } + ); + } else if (!policy.isValid(offset, length, this.length)) { + throw policy.makeException(this, offset, length); } } - @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 checkSliceBounds(long offset, long length) { + checkBounds(offset, length, SLICE_POLICY); + } + + @ForceInline + void checkAccessBounds(long offset, long length) { + checkBounds(offset, length, ACCESS_POLICY); } @Override @@ -429,13 +436,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; " + - "attempting to access an element of length %d at offset %d " + - "which is outside the valid range 0 <= offset+length < byteSize (=%d)", - this, length, offset, byteSize())); - } - static class SegmentSplitter implements Spliterator { AbstractMemorySegmentImpl segment; long elemCount; @@ -510,6 +510,46 @@ public int characteristics() { } } + private interface BoundPolicy { + boolean isValid(long offset, long length, long totalLength); + + String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length); + + default IndexOutOfBoundsException makeException(AbstractMemorySegmentImpl segment, long offset, long length) { + return new IndexOutOfBoundsException(formatExceptionMessage(segment, offset, length)); + } + } + + private static final BoundPolicy ACCESS_POLICY = new BoundPolicy() { + @Override + public boolean isValid(long offset, long length, long totalLength) { + return length > 0 && offset >= 0 && offset <= totalLength - length; + } + + @Override + public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length) { + return String.format("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)", + segment, length, offset, segment.byteSize()); + } + }; + + private final static BoundPolicy SLICE_POLICY = new BoundPolicy() { + @Override + public boolean isValid(long offset, long length, long totalLength) { + return length >= 0 && offset >= 0 && offset <= totalLength - length; + } + + @Override + public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length) { + return String.format("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)", + segment, length, offset, 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..1ceca21253fc9 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); } } From 53f68ba981540a6e90878fea82a1ea27c01a652e Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Tue, 11 Nov 2025 01:32:52 +0200 Subject: [PATCH 3/9] Remove the length > 0 condition, as it brakes multiple unit tests --- .../foreign/AbstractMemorySegmentImpl.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) 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 e28e5dc0111f0..c150465e60f2f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -406,8 +406,8 @@ private void checkBounds(long offset, long length, BoundPolicy policy) { return policy.makeException(this, off, len); } ); - } else if (!policy.isValid(offset, length, this.length)) { - throw policy.makeException(this, offset, length); + } else { + policy.validate(this, offset, length); } } @@ -511,7 +511,15 @@ public int characteristics() { } private interface BoundPolicy { - boolean isValid(long offset, long length, long totalLength); + default void validate(AbstractMemorySegmentImpl segment, long offset, long length) { + if (!isValid(offset, length, segment.length)) { + throw makeException(segment, offset, length); + } + } + + default boolean isValid(long offset, long length, long totalLength) { + return length >= 0 && offset >= 0 && offset <= totalLength - length; + } String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length); @@ -521,11 +529,6 @@ default IndexOutOfBoundsException makeException(AbstractMemorySegmentImpl segmen } private static final BoundPolicy ACCESS_POLICY = new BoundPolicy() { - @Override - public boolean isValid(long offset, long length, long totalLength) { - return length > 0 && offset >= 0 && offset <= totalLength - length; - } - @Override public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length) { return String.format("Out of bound access on segment %s; " + @@ -536,11 +539,6 @@ public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long off }; private final static BoundPolicy SLICE_POLICY = new BoundPolicy() { - @Override - public boolean isValid(long offset, long length, long totalLength) { - return length >= 0 && offset >= 0 && offset <= totalLength - length; - } - @Override public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length) { return String.format("Out of bound access on segment %s; " + From fb7b28c09c59f4994e6b4d07c031a1ed1d796df8 Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Tue, 11 Nov 2025 02:01:31 +0200 Subject: [PATCH 4/9] missed condition fix --- .../classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java | 2 +- test/jdk/java/foreign/TestSegments.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 c150465e60f2f..4120ea47ab1df 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -533,7 +533,7 @@ default IndexOutOfBoundsException makeException(AbstractMemorySegmentImpl segmen public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length) { return String.format("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)", + "which is outside the valid range 0 <= offset+length < byteSize (=%d)", segment, length, offset, segment.byteSize()); } }; diff --git a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java index 1ceca21253fc9..e9f3e8a87cc21 100644 --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -220,7 +220,7 @@ public void testSegmentAccessOOBMessage() { } catch (IndexOutOfBoundsException ex) { 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)")); + "which is outside the valid range 0 <= offset+length < byteSize (=10)")); } catch (Exception ex) { fail("Unexpected exception type thrown: " + ex); } From b96a0152080a08247d6b28a8a79f62e0530f322b Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Sat, 15 Nov 2025 00:23:24 +0200 Subject: [PATCH 5/9] rework --- .../foreign/AbstractMemorySegmentImpl.java | 80 +++++++------------ 1 file changed, 28 insertions(+), 52 deletions(-) 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 4120ea47ab1df..6a5e2cecab830 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -397,28 +397,14 @@ private int checkArraySize(String typeName, int elemSize) { return (int)arraySize; } - @ForceInline - private void checkBounds(long offset, long length, BoundPolicy policy) { - if (length > 0) { - Preconditions.checkIndex(offset, this.length - length + 1, (s, nums) -> { - long off = nums.get(0).longValue(); - long len = this.byteSize() - nums.get(1).longValue() + 1; - return policy.makeException(this, off, len); - } - ); - } else { - policy.validate(this, offset, length); - } - } - @ForceInline void checkSliceBounds(long offset, long length) { - checkBounds(offset, length, SLICE_POLICY); + Preconditions.checkFromIndexSize(offset, length, this.length, this::sliceOutOfBoundException); } @ForceInline void checkAccessBounds(long offset, long length) { - checkBounds(offset, length, ACCESS_POLICY); + Preconditions.checkFromIndexSize(offset, length, this.length, this::accessOutOfBoundException); } @Override @@ -436,6 +422,30 @@ public final MemorySessionImpl sessionImpl() { return scope; } + private IndexOutOfBoundsException sliceOutOfBoundException(String s, List numbers) { + return outOfBoundException(BoundPolicy.SLICE, numbers); + } + + private IndexOutOfBoundsException accessOutOfBoundException(String s, List numbers) { + return outOfBoundException(BoundPolicy.ACCESS, numbers); + } + + private IndexOutOfBoundsException outOfBoundException(BoundPolicy policy, List numbers) { + long offset = numbers.get(0).longValue(); + long length = numbers.get(1).longValue(); + long totalLength = numbers.get(2).longValue(); + + String msg = switch (policy) { + case BoundPolicy.SLICE -> "attempting to get slice of length"; + case BoundPolicy.ACCESS -> "attempting to access an element of length"; + }; + + return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; " + + "%s %d at offset %d " + + "which is outside the valid range 0 <= offset+length < byteSize (=%d)", + this, msg, length, offset, totalLength)); + } + static class SegmentSplitter implements Spliterator { AbstractMemorySegmentImpl segment; long elemCount; @@ -510,44 +520,10 @@ public int characteristics() { } } - private interface BoundPolicy { - default void validate(AbstractMemorySegmentImpl segment, long offset, long length) { - if (!isValid(offset, length, segment.length)) { - throw makeException(segment, offset, length); - } - } - - default boolean isValid(long offset, long length, long totalLength) { - return length >= 0 && offset >= 0 && offset <= totalLength - length; - } - - String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length); - - default IndexOutOfBoundsException makeException(AbstractMemorySegmentImpl segment, long offset, long length) { - return new IndexOutOfBoundsException(formatExceptionMessage(segment, offset, length)); - } + private enum BoundPolicy { + SLICE, ACCESS } - private static final BoundPolicy ACCESS_POLICY = new BoundPolicy() { - @Override - public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length) { - return String.format("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)", - segment, length, offset, segment.byteSize()); - } - }; - - private final static BoundPolicy SLICE_POLICY = new BoundPolicy() { - @Override - public String formatExceptionMessage(AbstractMemorySegmentImpl segment, long offset, long length) { - return String.format("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)", - segment, length, offset, segment.byteSize()); - } - }; - // Object methods @Override From 9a2cc1388975e622e6c7f07c4446d3360c3dc78f Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Sat, 15 Nov 2025 15:33:15 +0200 Subject: [PATCH 6/9] rework: eliminate lambda binding; introduce lightweight wrapper which should be eliminated by escape analysis --- .../foreign/AbstractMemorySegmentImpl.java | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) 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 6a5e2cecab830..1ebaa7e222d54 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -399,12 +399,12 @@ private int checkArraySize(String typeName, int elemSize) { @ForceInline void checkSliceBounds(long offset, long length) { - Preconditions.checkFromIndexSize(offset, length, this.length, this::sliceOutOfBoundException); + Preconditions.checkFromIndexSize(offset, length, this.length, new BoundsCheckHandler(this, BoundPolicy.SLICE)); } @ForceInline void checkAccessBounds(long offset, long length) { - Preconditions.checkFromIndexSize(offset, length, this.length, this::accessOutOfBoundException); + Preconditions.checkFromIndexSize(offset, length, this.length, new BoundsCheckHandler(this, BoundPolicy.ACCESS)); } @Override @@ -422,30 +422,6 @@ public final MemorySessionImpl sessionImpl() { return scope; } - private IndexOutOfBoundsException sliceOutOfBoundException(String s, List numbers) { - return outOfBoundException(BoundPolicy.SLICE, numbers); - } - - private IndexOutOfBoundsException accessOutOfBoundException(String s, List numbers) { - return outOfBoundException(BoundPolicy.ACCESS, numbers); - } - - private IndexOutOfBoundsException outOfBoundException(BoundPolicy policy, List numbers) { - long offset = numbers.get(0).longValue(); - long length = numbers.get(1).longValue(); - long totalLength = numbers.get(2).longValue(); - - String msg = switch (policy) { - case BoundPolicy.SLICE -> "attempting to get slice of length"; - case BoundPolicy.ACCESS -> "attempting to access an element of length"; - }; - - return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; " + - "%s %d at offset %d " + - "which is outside the valid range 0 <= offset+length < byteSize (=%d)", - this, msg, length, offset, totalLength)); - } - static class SegmentSplitter implements Spliterator { AbstractMemorySegmentImpl segment; long elemCount; @@ -521,7 +497,48 @@ public int characteristics() { } private enum BoundPolicy { - SLICE, ACCESS + SLICE { + @Override + String format(AbstractMemorySegmentImpl segment, long offset, long size, long length) { + return String.format( + "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)", + segment, size, offset, length + ); + } + }, + ACCESS { + @Override + String format(AbstractMemorySegmentImpl segment, long offset, long size, long length) { + return String.format( + "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)", + segment, size, offset, length + ); + } + }; + + abstract String format(AbstractMemorySegmentImpl segment, long offset, long size, long length); + } + + private static final class BoundsCheckHandler implements BiFunction, IndexOutOfBoundsException> { + final AbstractMemorySegmentImpl segment; + final BoundPolicy policy; + + BoundsCheckHandler(AbstractMemorySegmentImpl segment, BoundPolicy policy) { + this.segment = segment; + this.policy = policy; + } + + @Override + public IndexOutOfBoundsException apply(String s, List args) { + long offset = args.get(0).longValue(); + long size = args.get(1).longValue(); + long length = args.get(2).longValue(); + + String msg = policy.format(segment, offset, size, length); + return new IndexOutOfBoundsException(msg); + } } // Object methods From df86a856cd1993bf052aab1cb893ea4ebe507c84 Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Sun, 16 Nov 2025 16:46:01 +0200 Subject: [PATCH 7/9] Adjust to TestMergeStoresMemorySegment.java requirements --- .../foreign/AbstractMemorySegmentImpl.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) 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 1ebaa7e222d54..2edf66057aa0b 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -399,12 +399,21 @@ private int checkArraySize(String typeName, int elemSize) { @ForceInline void checkSliceBounds(long offset, long length) { - Preconditions.checkFromIndexSize(offset, length, this.length, new BoundsCheckHandler(this, BoundPolicy.SLICE)); + checkBounds(offset, length, BoundPolicy.SLICE); } @ForceInline void checkAccessBounds(long offset, long length) { - Preconditions.checkFromIndexSize(offset, length, this.length, new BoundsCheckHandler(this, BoundPolicy.ACCESS)); + 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 @@ -518,6 +527,11 @@ String format(AbstractMemorySegmentImpl segment, long offset, long size, long le } }; + private IndexOutOfBoundsException outOfBoundException(AbstractMemorySegmentImpl segment, long offset, long size, + long length) { + return new IndexOutOfBoundsException(format(segment, offset, size, length)); + } + abstract String format(AbstractMemorySegmentImpl segment, long offset, long size, long length); } @@ -531,13 +545,11 @@ private static final class BoundsCheckHandler implements BiFunction args) { - long offset = args.get(0).longValue(); - long size = args.get(1).longValue(); - long length = args.get(2).longValue(); + public IndexOutOfBoundsException apply(String s, List numbers) { + long offset = numbers.get(0).longValue(); + long length = segment.byteSize() - numbers.get(1).longValue() + 1; - String msg = policy.format(segment, offset, size, length); - return new IndexOutOfBoundsException(msg); + return policy.outOfBoundException(segment, offset, length, segment.byteSize()); } } From 1095cf6e64a317d12214b97fbb54e7b20cef6452 Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Tue, 18 Nov 2025 11:33:14 +0200 Subject: [PATCH 8/9] improvements according to reviewer comments --- .../foreign/AbstractMemorySegmentImpl.java | 43 +++++-------------- 1 file changed, 11 insertions(+), 32 deletions(-) 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 2edf66057aa0b..1cc1c54041eb0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -506,44 +506,23 @@ public int characteristics() { } private enum BoundPolicy { - SLICE { - @Override - String format(AbstractMemorySegmentImpl segment, long offset, long size, long length) { - return String.format( - "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)", - segment, size, offset, length - ); - } - }, - ACCESS { - @Override - String format(AbstractMemorySegmentImpl segment, long offset, long size, long length) { - return String.format( - "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)", - segment, size, offset, length - ); - } - }; + ACCESS, SLICE; private IndexOutOfBoundsException outOfBoundException(AbstractMemorySegmentImpl segment, long offset, long size, long length) { - return new IndexOutOfBoundsException(format(segment, offset, size, 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); } - - abstract String format(AbstractMemorySegmentImpl segment, long offset, long size, long length); } - private static final class BoundsCheckHandler implements BiFunction, IndexOutOfBoundsException> { - final AbstractMemorySegmentImpl segment; - final BoundPolicy policy; - - BoundsCheckHandler(AbstractMemorySegmentImpl segment, BoundPolicy policy) { - this.segment = segment; - this.policy = policy; - } - + private record BoundsCheckHandler(AbstractMemorySegmentImpl segment, BoundPolicy policy) + implements BiFunction, IndexOutOfBoundsException> { @Override public IndexOutOfBoundsException apply(String s, List numbers) { long offset = numbers.get(0).longValue(); From 57b94806e2fe31ab3390cddfce49037b0b2822bd Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Fri, 21 Nov 2025 19:20:22 +0200 Subject: [PATCH 9/9] implement re-throwing approach according to reviewer recomandation --- .../foreign/AbstractMemorySegmentImpl.java | 52 +++++++------------ 1 file changed, 19 insertions(+), 33 deletions(-) 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 1cc1c54041eb0..1b87eea4ebf2b 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -399,20 +399,33 @@ private int checkArraySize(String typeName, int elemSize) { @ForceInline void checkSliceBounds(long offset, long length) { - checkBounds(offset, length, BoundPolicy.SLICE); + try { + checkBounds(offset, length); + } catch (IndexOutOfBoundsException e) { + String msg = String.format("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)", this, length, offset, this.length); + throw new IndexOutOfBoundsException(msg); + } } @ForceInline void checkAccessBounds(long offset, long length) { - checkBounds(offset, length, BoundPolicy.ACCESS); + try { + checkBounds(offset, length); + } catch (IndexOutOfBoundsException e) { + String msg = String.format("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)", this, length, offset, this.length); + throw new IndexOutOfBoundsException(msg); + } } @ForceInline - private void checkBounds(long offset, long length, BoundPolicy policy) { + private void checkBounds(long offset, long length) { 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); + Preconditions.checkIndex(offset, this.length - length + 1, null); + } else if (length < 0 || offset < 0 || + offset > this.length - length) { + throw new IndexOutOfBoundsException(); } } @@ -505,33 +518,6 @@ 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