Skip to content
Permalink
Browse files
Automatic merge of foreign-memaccess into foreign-abi
  • Loading branch information
duke committed May 11, 2020
2 parents e95c374 + 1bd6563 commit 39a8e0103919fd59d6d7b87ece60f17d8d6b6da6
Showing with 130 additions and 28 deletions.
  1. +24 −4 src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java
  2. +106 −24 test/jdk/java/foreign/TestLayoutPaths.java
@@ -97,7 +97,7 @@
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
* <p>
* Layout paths are for example useful in order to obtain offsets of arbitrarily nested layouts inside another layout
* (see {@link MemoryLayout#offset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected
* (see {@link MemoryLayout#bitOffset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}), to select an arbitrarily nested layout inside
* another layout (see {@link MemoryLayout#select(PathElement...)}, or to transform a nested layout element inside
* another layout (see {@link MemoryLayout#map(UnaryOperator, PathElement...)}).
@@ -112,9 +112,9 @@
));
* }</pre></blockquote>
*
* We can obtain the offset of the member layout named <code>value</code> from <code>seq</code>, as follows:
* We can obtain the offset, in bits, of the member layout named <code>value</code> from <code>seq</code>, as follows:
* <blockquote><pre>{@code
long valueOffset = seq.addOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
long valueOffset = seq.bitOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
* }</pre></blockquote>
*
* Similarly, we can select the member layout named {@code value}, as follows:
@@ -323,10 +323,30 @@ default long byteAlignment() {
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
* @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
*/
default long offset(PathElement... elements) {
default long bitOffset(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offset, EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements);
}

/**
* Computes the offset, in bytes, of the layout selected by a given layout path, where the path is considered rooted in this
* layout.
*
* @apiNote if the layout path has one (or more) free dimensions,
* the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
*
* @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 UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size,
* or if {@code bitOffset(elements)} is not a multiple of 8.
*/
default long byteOffset(PathElement... elements) {
return Utils.bitsToBytesOrThrow(bitOffset(elements),
() -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8"));
}

/**
* Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
* where the path is considered rooted in this layout.
@@ -36,64 +36,126 @@
import org.testng.annotations.*;

import java.util.List;
import java.util.function.Function;

import static org.testng.Assert.*;

public class TestLayoutPaths {

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromSeq() {
public void testBadBitSelectFromSeq() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.offset(PathElement.groupElement("foo"));
seq.bitOffset(PathElement.groupElement("foo"));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromStruct() {
public void testBadByteSelectFromSeq() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.byteOffset(PathElement.groupElement("foo"));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadBitSelectFromStruct() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.bitOffset(PathElement.sequenceElement());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadByteSelectFromStruct() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.sequenceElement());
g.byteOffset(PathElement.sequenceElement());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadBitSelectFromValue() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromValue() {
public void testBadByteSelectFromValue() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(), PathElement.sequenceElement());
seq.byteOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnknownStructField() {
public void testUnknownBitStructField() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.groupElement("foo"));
g.bitOffset(PathElement.groupElement("foo"));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnknownByteStructField() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.byteOffset(PathElement.groupElement("foo"));
}

@Test(expectedExceptions = NullPointerException.class)
public void testNullGroupElementName() {
PathElement.groupElement(null);
}

@Test(expectedExceptions = NullPointerException.class)
public void testBitNullGroupElementName() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.bitOffset(PathElement.groupElement(null));
}

@Test(expectedExceptions = NullPointerException.class)
public void testByteNullGroupElementName() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.groupElement(null));
g.byteOffset(PathElement.groupElement(null));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitOutOfBoundsSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(6));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testOutOfBoundsSeqIndex() {
public void testByteOutOfBoundsSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(6));
seq.byteOffset(PathElement.sequenceElement(6));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqIndex() {
PathElement.sequenceElement(-2);
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitNegativeSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(-2));
seq.bitOffset(PathElement.sequenceElement(-2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteNegativeSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.byteOffset(PathElement.sequenceElement(-2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testOutOfBoundsSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(6, 2));
seq.bitOffset(PathElement.sequenceElement(6, 2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqRange() {
PathElement.sequenceElement(-2, 2);
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitNegativeSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(-2, 2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteNegativeSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(-2, 2));
seq.byteOffset(PathElement.sequenceElement(-2, 2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
@@ -102,11 +164,18 @@ public void testIncompleteAccess() {
seq.varHandle(int.class, PathElement.sequenceElement());
}

@Test(expectedExceptions = UnsupportedOperationException.class)
public void testBadMultiple() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(3), MemoryLayouts.JAVA_INT.withName("foo"));
g.byteOffset(PathElement.groupElement("foo"));
}

@Test
public void testBadContainerAlign() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
try {
g.offset(PathElement.groupElement("foo"));
g.bitOffset(PathElement.groupElement("foo"));
g.byteOffset(PathElement.groupElement("foo"));
} catch (Throwable ex) {
throw new AssertionError(ex); // should be ok!
}
@@ -124,7 +193,8 @@ public void testBadContainerAlign() {
public void testBadAlignOffset() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo"));
try {
g.offset(PathElement.groupElement("foo"));
g.bitOffset(PathElement.groupElement("foo"));
g.byteOffset(PathElement.groupElement("foo"));
} catch (Throwable ex) {
throw new AssertionError(ex); // should be ok!
}
@@ -144,7 +214,13 @@ public void testBadSequencePathInOffset() {
// bad path elements
for (PathElement e : List.of( PathElement.sequenceElement(), PathElement.sequenceElement(0, 2) )) {
try {
seq.offset(e);
seq.bitOffset(e);
fail();
} catch (IllegalArgumentException ex) {
assertTrue(true);
}
try {
seq.byteOffset(e);
fail();
} catch (IllegalArgumentException ex) {
assertTrue(true);
@@ -198,8 +274,10 @@ public void testStructPaths() {
// test offset

for (int i = 1 ; i <= 4 ; i++) {
long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], offset);
long bitOffset = g.bitOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], bitOffset);
long byteOffset = g.byteOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals((offsets[i - 1]) >>> 3, byteOffset);
}

// test map
@@ -237,8 +315,10 @@ public void testUnionPaths() {
// test offset

for (int i = 1 ; i <= 4 ; i++) {
long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], offset);
long bitOffset = g.bitOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], bitOffset);
long byteOffset = g.byteOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals((offsets[i - 1]) >>> 3, byteOffset);
}

// test map
@@ -269,8 +349,10 @@ public void testSequencePaths() {
// test offset

for (int i = 0 ; i < 4 ; i++) {
long offset = g.offset(PathElement.sequenceElement(i));
assertEquals(offsets[i], offset);
long bitOffset = g.bitOffset(PathElement.sequenceElement(i));
assertEquals(offsets[i], bitOffset);
long byteOffset = g.byteOffset(PathElement.sequenceElement(i));
assertEquals((offsets[i]) >>> 3, byteOffset);
}

// test map

0 comments on commit 39a8e01

Please sign in to comment.