changeset 57754:20c8599c64b7

8235837: Memory access API refinements Reviewed-by: chegar, psandoz
author mcimadamore
date Wed, 15 Jan 2020 11:09:00 +0000
parents a8680d72a2bf
children 5b2e2fdb4d19
files src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/AbstractLayout.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SequenceLayout.java src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemorySegmentImpl.java test/jdk/java/foreign/TestLayoutPaths.java test/jdk/java/foreign/TestLayouts.java test/jdk/java/foreign/TestMemoryAccess.java test/jdk/java/foreign/TestMemoryAlignment.java test/jdk/java/foreign/TestMemoryCopy.java test/jdk/java/foreign/TestSegments.java test/jdk/java/foreign/TestVarHandleCombinators.java
diffstat 16 files changed, 422 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/AbstractLayout.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/AbstractLayout.java	Wed Jan 15 11:09:00 2020 +0000
@@ -93,6 +93,11 @@
     }
 
     @Override
+    public boolean hasSize() {
+        return size.isPresent();
+    }
+
+    @Override
     public long bitSize() {
         return size.orElseThrow(this::badSizeException);
     }
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java	Wed Jan 15 11:09:00 2020 +0000
@@ -31,7 +31,7 @@
 /**
  * A memory address encodes an offset within a given {@link MemorySegment}. Memory addresses are typically obtained
  * using the {@link MemorySegment#baseAddress()} method; such addresses can then be adjusted as required,
- * using {@link MemoryAddress#offset(long)}.
+ * using {@link MemoryAddress#addOffset(long)}.
  * <p>
  * A memory address is typically used as the first argument in a memory access var handle call, to perform some operation
  * on the underlying memory backing a given memory segment. Since a memory address is always associated with a memory segment,
@@ -53,11 +53,11 @@
  */
 public interface MemoryAddress {
     /**
-     * Creates a new memory address with given offset (in bytes) from current one.
-     * @param l specified offset (in bytes), relative to this address, which should be used to create the new address.
+     * Creates a new memory address with given offset (in bytes), which might be negative, from current one.
+     * @param offset specified offset (in bytes), relative to this address, which should be used to create the new address.
      * @return a new memory address with given offset from current one.
      */
-    MemoryAddress offset(long l);
+    MemoryAddress addOffset(long offset);
 
     /**
      * The offset of this memory address into the underlying segment.
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java	Wed Jan 15 11:09:00 2020 +0000
@@ -26,16 +26,21 @@
 package jdk.incubator.foreign;
 
 import jdk.internal.foreign.LayoutPath;
+import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind;
 import jdk.internal.foreign.Utils;
 
 import java.lang.constant.Constable;
 import java.lang.constant.DynamicConstantDesc;
 import java.lang.invoke.VarHandle;
 import java.nio.ByteOrder;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.OptionalLong;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
 
 /**
  * A memory layout can be used to describe the contents of a memory segment in a <em>language neutral</em> fashion.
@@ -87,9 +92,11 @@
  * at a layout nested within the root layout - this is the layout <em>selected</em> by the layout path.
  * Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
  * <p>
- * Layout paths are useful in order to e.g. to obtain offset of leaf elements inside arbitrarily nested layouts
- * (see {@link MemoryLayout#offset(PathElement...)}), or to quickly obtain a memory access handle corresponding to the selected
- * layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}).
+ * 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
+ * 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...)}).
  * <p>
  * Such <em>layout paths</em> can be constructed programmatically using the methods in this class.
  * For instance, given a layout constructed as follows:
@@ -106,11 +113,37 @@
 long valueOffset = seq.offset(PathElement.sequenceElement(), PathElement.groupElement("value"));
  * }</pre></blockquote>
  *
+ * Similarly, we can select the member layout named {@code value}, as follows:
+ * <blockquote><pre>{@code
+MemoryLayout value = seq.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
+ * }</pre></blockquote>
+ *
+ * And, we can also replace the layout named {@code value} with another layout, as follows:
+ * <blockquote><pre>{@code
+MemoryLayout newSeq = seq.map(l -> MemoryLayout.ofPadding(32), PathElement.sequenceElement(), PathElement.groupElement("value"));
+ * }</pre></blockquote>
+ *
+ * That is, the above declaration is identical to the following, more verbose one:
+ * <blockquote><pre>{@code
+MemoryLayout newSeq = MemoryLayout.ofSequence(5,
+    MemoryLayout.ofStruct(
+        MemoryLayout.ofPaddingBits(32),
+        MemoryLayout.ofPaddingBits(32)
+));
+ * }</pre></blockquote>
+ *
  * Layout paths can feature one or more <em>free dimensions</em>. 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;
- * that is, the memory access var handle associated with such a layout path expression will feature an extra {@code long}
- * access coordinate. The layout path constructed in the above example features exactly one free dimension.
+ * {@link PathElement#sequenceElement()} method) features an additional free dimension, which will have to be bound at runtime.
+ * This is important when obtaining memory access var handle from layouts, as in the following code:
+ *
+ * <blockquote><pre>{@code
+VarHandle valueHandle = seq.map(int.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
+ * }</pre></blockquote>
+ *
+ * Since the layout path {@code seq} constructed in the above example features exactly one free dimension,
+ * it follows that the memory access var handle {@code valueHandle} will feature an extra {@code long}
+ * access coordinate.
  *
  * @apiNote In the future, if the Java language permits, {@link MemoryLayout}
  * may become a {@code sealed} interface, which would prohibit subclassing except by
@@ -133,6 +166,17 @@
     Optional<? extends DynamicConstantDesc<? extends MemoryLayout>> describeConstable();
 
     /**
+     * Does this layout have a specified size? A layout does not have a specified size if it is (or contains) a sequence layout whose
+     * size is unspecified (see {@link SequenceLayout#elementCount()}).
+     *
+     * Value layouts (see {@link ValueLayout}) and padding layouts (see {@link MemoryLayout#ofPaddingBits(long)})
+     * <em>always</em> have a specified size, therefore this method always returns {@code true} in these cases.
+     *
+     * @return {@code true}, if this layout has a specified size.
+     */
+    boolean hasSize();
+
+    /**
      * Computes the layout size, in bits.
      *
      * @return the layout size, in bits.
@@ -219,23 +263,21 @@
     MemoryLayout withBitAlignment(long bitAlignment);
 
     /**
-     * Computes the offset of the layout selected by a given layout path, where the path is considered rooted in this
+     * Computes the offset, in bits, 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 of layout selected by a the layout path obtained by concatenating the path elements in {@code elements}.
-     * @throws IllegalArgumentException if the layout path obtained by concatenating the path elements in {@code elements}
-     * does not select a valid layout element.
+     * @return The offset, in bits, 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.
      */
     default long offset(PathElement... elements) {
-        LayoutPath path = LayoutPath.rootPath(this);
-        for (PathElement e : elements) {
-            path = ((LayoutPath.PathElementImpl)e).apply(path);
-        }
-        return path.offset();
+        return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offset, EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements);
     }
 
     /**
@@ -248,19 +290,59 @@
      *
      * @param carrier the var handle carrier type.
      * @param elements the layout path elements.
-     * @return a var handle which can be used to dereference memory at the layout denoted by given layout path.
-     * @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints.
+     * @return a var handle which can be used to dereference memory 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 constraints,
+     * or if one of the layouts traversed by the layout path has unspecified size.
      * @throws IllegalArgumentException if the carrier does not represent a primitive type, if the carrier is {@code void},
-     * {@code boolean}, or if the layout path obtained by concatenating the path elements in {@code elements}
-     * does not select a value layout (see {@link ValueLayout}), or if the selected value layout has a size that
-     * that does not match that of the specified carrier type.
+     * {@code boolean}, or if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}),
+     * or if the selected value layout has a size that that does not match that of the specified carrier type.
      */
     default VarHandle varHandle(Class<?> carrier, PathElement... elements) {
-        LayoutPath path = LayoutPath.rootPath(this);
+        return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), path -> path.dereferenceHandle(carrier),
+                Set.of(), elements);
+    }
+
+    /**
+     * Selects the layout from a path rooted in 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)}).
+     */
+    default MemoryLayout select(PathElement... elements) {
+        return computePathOp(LayoutPath.rootPath(this, l -> 0L), LayoutPath::layout,
+                EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE), elements);
+    }
+
+    /**
+     * Creates a transformed copy of this layout where a selected layout, from a path rooted in this layout,
+     * is replaced with the result of applying the given operation.
+     *
+     * @param op the unary operation to be applied to the selected layout.
+     * @param elements the layout path elements.
+     * @return a new layout where the layout selected by the layout path in {@code elements},
+     * has been replaced by the result of applying {@code op} to the selected layout.
+     * @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)}).
+     */
+    default MemoryLayout map(UnaryOperator<MemoryLayout> op, PathElement... elements) {
+        return computePathOp(LayoutPath.rootPath(this, l -> 0L), path -> path.map(op),
+                EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE), elements);
+    }
+
+    private static <Z> Z computePathOp(LayoutPath path, Function<LayoutPath, Z> finalizer,
+                                       Set<LayoutPath.PathElementImpl.PathKind> badKinds, PathElement... elements) {
         for (PathElement e : elements) {
-            path = ((LayoutPath.PathElementImpl)e).apply(path);
+            LayoutPath.PathElementImpl pathElem = (LayoutPath.PathElementImpl)e;
+            if (badKinds.contains(pathElem.kind())) {
+                throw new IllegalArgumentException(String.format("Invalid %s selection in layout path", pathElem.kind().description()));
+            }
+            path = pathElem.apply(path);
         }
-        return path.dereferenceHandle(carrier);
+        return finalizer.apply(path);
     }
 
     /**
@@ -297,7 +379,8 @@
          */
         static PathElement groupElement(String name) {
             Objects.requireNonNull(name);
-            return new LayoutPath.PathElementImpl(path -> path.groupElement(name));
+            return new LayoutPath.PathElementImpl(LayoutPath.PathElementImpl.PathKind.GROUP_ELEMENT,
+                                                  path -> path.groupElement(name));
         }
 
         /**
@@ -313,7 +396,8 @@
             if (index < 0) {
                 throw new IllegalArgumentException("Index must be positive: " + index);
             }
-            return new LayoutPath.PathElementImpl(path -> path.sequenceElement(index));
+            return new LayoutPath.PathElementImpl(LayoutPath.PathElementImpl.PathKind.SEQUENCE_ELEMENT_INDEX,
+                                                  path -> path.sequenceElement(index));
         }
 
         /**
@@ -341,7 +425,8 @@
             if (step == 0) {
                 throw new IllegalArgumentException("Step must be != 0: " + step);
             }
-            return new LayoutPath.PathElementImpl(path -> path.sequenceElement(start, step));
+            return new LayoutPath.PathElementImpl(LayoutPath.PathElementImpl.PathKind.SEQUENCE_RANGE,
+                                                  path -> path.sequenceElement(start, step));
         }
 
         /**
@@ -352,7 +437,8 @@
          * @return a path element which selects an unspecified sequence element layout.
          */
         static PathElement sequenceElement() {
-            return new LayoutPath.PathElementImpl(LayoutPath::sequenceElement);
+            return new LayoutPath.PathElementImpl(LayoutPath.PathElementImpl.PathKind.SEQUENCE_ELEMENT,
+                                                  LayoutPath::sequenceElement);
         }
     }
 
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java	Wed Jan 15 11:09:00 2020 +0000
@@ -32,7 +32,7 @@
  * This class defines useful layout constants. Some of the constants defined in this class are explicit in both
  * size and byte order (see {@link #BITS_64_BE}), and can therefore be used to explicitly and unambiguously specify the
  * contents of a memory segment. Other constants make implicit byte order assumptions (see
- * {@link #JAVA_INT}); as such, these constants make it easy to interoperate with other serialization-centric APIs,
+ * {@link #JAVA_INT}); as such, these constants make it easy to work with other serialization-centric APIs,
  * such as {@link java.nio.ByteBuffer}.
  */
 public final class MemoryLayouts {
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java	Wed Jan 15 11:09:00 2020 +0000
@@ -145,10 +145,10 @@
     MemorySegment acquire();
 
     /**
-     * Is this segment accessible from the current thread?
-     * @return true, if this segment is accessible from the current thread.
+     * The thread owning this segment.
+     * @return the thread owning this segment.
      */
-    boolean isAccessible();
+    Thread ownerThread();
 
     /**
      * The size (in bytes) of this memory segment.
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SequenceLayout.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SequenceLayout.java	Wed Jan 15 11:09:00 2020 +0000
@@ -95,6 +95,18 @@
         return elemCount;
     }
 
+    /**
+     * Obtains a new sequence layout with same element layout, alignment constraints and name as this sequence layout
+     * but with the new specified element count.
+     * @param elementCount the new element count.
+     * @return a new sequence with given element count.
+     * @throws IllegalArgumentException if {@code elementCount < 0}.
+     */
+    public SequenceLayout withElementCount(long elementCount) {
+        AbstractLayout.checkSize(elementCount, true);
+        return new SequenceLayout(OptionalLong.of(elementCount), elementLayout, alignment, name());
+    }
+
     @Override
     public String toString() {
         return decorateLayoutString(String.format("[%s:%s]",
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java	Wed Jan 15 11:09:00 2020 +0000
@@ -34,6 +34,9 @@
 import jdk.incubator.foreign.SequenceLayout;
 import jdk.incubator.foreign.ValueLayout;
 import java.lang.invoke.VarHandle;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.ToLongFunction;
 import java.util.function.UnaryOperator;
 import java.util.stream.LongStream;
 
@@ -53,12 +56,16 @@
     private final long offset;
     private final LayoutPath enclosing;
     private final long[] strides;
+    private final long elementIndex;
+    private final ToLongFunction<MemoryLayout> sizeFunc;
 
-    private LayoutPath(MemoryLayout layout, long offset, long[] strides, LayoutPath enclosing) {
+    private LayoutPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath enclosing, ToLongFunction<MemoryLayout> sizeFunc) {
         this.layout = layout;
         this.offset = offset;
         this.strides = strides;
         this.enclosing = enclosing;
+        this.elementIndex = elementIndex;
+        this.sizeFunc = sizeFunc;
     }
 
     // Layout path selector methods
@@ -67,7 +74,7 @@
         check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
         SequenceLayout seq = (SequenceLayout)layout;
         MemoryLayout elem = seq.elementLayout();
-        return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), this);
+        return LayoutPath.nestedPath(elem, offset, addStride(sizeFunc.applyAsLong(elem)), -1, this);
     }
 
     public LayoutPath sequenceElement(long start, long step) {
@@ -75,8 +82,8 @@
         SequenceLayout seq = (SequenceLayout)layout;
         checkSequenceBounds(seq, start);
         MemoryLayout elem = seq.elementLayout();
-        long elemSize = elem.bitSize();
-        return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), this);
+        long elemSize = sizeFunc.applyAsLong(elem);
+        return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), -1, this);
     }
 
     public LayoutPath sequenceElement(long index) {
@@ -86,10 +93,10 @@
         long elemOffset = 0;
         if (index > 0) {
             //if index == 0, we do not depend on sequence element size, so skip
-            long elemSize = seq.elementLayout().bitSize();
+            long elemSize = sizeFunc.applyAsLong(seq.elementLayout());
             elemOffset = elemSize * index;
         }
-        return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, this);
+        return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, index, this);
     }
 
     public LayoutPath groupElement(String name) {
@@ -97,20 +104,22 @@
         GroupLayout g = (GroupLayout)layout;
         long offset = 0;
         MemoryLayout elem = null;
+        int index = -1;
         for (int i = 0; i < g.memberLayouts().size(); i++) {
             MemoryLayout l = g.memberLayouts().get(i);
             if (l.name().isPresent() &&
                 l.name().get().equals(name)) {
                 elem = l;
+                index = i;
                 break;
-            } else {
-                offset += l.bitSize();
+            } else if (g.isStruct()) {
+                offset += sizeFunc.applyAsLong(l);
             }
         }
         if (elem == null) {
             throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
         }
-        return LayoutPath.nestedPath(elem, this.offset + offset, strides, this);
+        return LayoutPath.nestedPath(elem, this.offset + offset, strides, index, this);
     }
 
     // Layout path projections
@@ -139,14 +148,52 @@
                 LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray());
     }
 
+    public MemoryLayout layout() {
+        return layout;
+    }
+
+    public MemoryLayout map(UnaryOperator<MemoryLayout> op) {
+        MemoryLayout newLayout = op.apply(layout);
+        if (enclosing == null) {
+            return newLayout;
+        } else if (enclosing.layout instanceof SequenceLayout) {
+            SequenceLayout seq = (SequenceLayout)enclosing.layout;
+            if (seq.elementCount().isPresent()) {
+                return enclosing.map(l -> dup(l, MemoryLayout.ofSequence(seq.elementCount().getAsLong(), newLayout)));
+            } else {
+                return enclosing.map(l -> dup(l, MemoryLayout.ofSequence(newLayout)));
+            }
+        } else if (enclosing.layout instanceof GroupLayout) {
+            GroupLayout g = (GroupLayout)enclosing.layout;
+            List<MemoryLayout> newElements = new ArrayList<>(g.memberLayouts());
+            //if we selected a layout in a group we must have a valid index
+            newElements.set((int)elementIndex, newLayout);
+            if (g.isUnion()) {
+                return enclosing.map(l -> dup(l, MemoryLayout.ofUnion(newElements.toArray(new MemoryLayout[0]))));
+            } else {
+                return enclosing.map(l -> dup(l, MemoryLayout.ofStruct(newElements.toArray(new MemoryLayout[0]))));
+            }
+        } else {
+            return newLayout;
+        }
+    }
+
+    private MemoryLayout dup(MemoryLayout oldLayout, MemoryLayout newLayout) {
+        newLayout = newLayout.withBitAlignment(oldLayout.bitAlignment());
+        if (oldLayout.name().isPresent()) {
+            newLayout.withName(oldLayout.name().get());
+        }
+        return newLayout;
+    }
+
     // Layout path construction
 
-    public static LayoutPath rootPath(MemoryLayout layout) {
-        return new LayoutPath(layout, 0L, EMPTY_STRIDES, null);
+    public static LayoutPath rootPath(MemoryLayout layout, ToLongFunction<MemoryLayout> sizeFunc) {
+        return new LayoutPath(layout, 0L, EMPTY_STRIDES, -1, null, sizeFunc);
     }
 
-    private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, LayoutPath encl) {
-        return new LayoutPath(layout, offset, strides, encl);
+    private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath encl) {
+        return new LayoutPath(layout, offset, strides, elementIndex, encl, encl.sizeFunc);
     }
 
     // Helper methods
@@ -202,9 +249,28 @@
      */
     public static class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
 
+        public enum PathKind {
+            SEQUENCE_ELEMENT("unbound sequence element"),
+            SEQUENCE_ELEMENT_INDEX("bound sequence element"),
+            SEQUENCE_RANGE("sequence range"),
+            GROUP_ELEMENT("group element");
+
+            final String description;
+
+            PathKind(String description) {
+                this.description = description;
+            }
+
+            public String description() {
+                return description;
+            }
+        }
+
+        final PathKind kind;
         final UnaryOperator<LayoutPath> pathOp;
 
-        public PathElementImpl(UnaryOperator<LayoutPath> pathOp) {
+        public PathElementImpl(PathKind kind, UnaryOperator<LayoutPath> pathOp) {
+            this.kind = kind;
             this.pathOp = pathOp;
         }
 
@@ -212,5 +278,9 @@
         public LayoutPath apply(LayoutPath layoutPath) {
             return pathOp.apply(layoutPath);
         }
+
+        public PathKind kind() {
+            return kind;
+        }
     }
 }
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java	Wed Jan 15 11:09:00 2020 +0000
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public MemoryAddress offset(long bytes) {
+    public MemoryAddress addOffset(long bytes) {
         return new MemoryAddressImpl(segment, offset + bytes);
     }
 
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemorySegmentImpl.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemorySegmentImpl.java	Wed Jan 15 11:09:00 2020 +0000
@@ -110,8 +110,8 @@
     }
 
     @Override
-    public boolean isAccessible() {
-        return owner == Thread.currentThread();
+    public Thread ownerThread() {
+        return owner;
     }
 
     @Override
--- a/test/jdk/java/foreign/TestLayoutPaths.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/test/jdk/java/foreign/TestLayoutPaths.java	Wed Jan 15 11:09:00 2020 +0000
@@ -34,6 +34,10 @@
 import jdk.incubator.foreign.SequenceLayout;
 
 import org.testng.annotations.*;
+
+import java.util.List;
+import java.util.function.Function;
+
 import static org.testng.Assert.*;
 
 public class TestLayoutPaths {
@@ -133,5 +137,146 @@
             throw new AssertionError(ex); //should fail!
         }
     }
+
+    @Test
+    public void testBadSequencePathInOffset() {
+        SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
+        // bad path elements
+        for (PathElement e : List.of( PathElement.sequenceElement(), PathElement.sequenceElement(0, 2) )) {
+            try {
+                seq.offset(e);
+                fail();
+            } catch (IllegalArgumentException ex) {
+                assertTrue(true);
+            }
+        }
+    }
+
+    @Test
+    public void testBadSequencePathInSelect() {
+        SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
+        for (PathElement e : List.of( PathElement.sequenceElement(0), PathElement.sequenceElement(0, 2) )) {
+            try {
+                seq.select(e);
+                fail();
+            } catch (IllegalArgumentException ex) {
+                assertTrue(true);
+            }
+        }
+    }
+
+    @Test
+    public void testBadSequencePathInMap() {
+        SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
+        for (PathElement e : List.of( PathElement.sequenceElement(0), PathElement.sequenceElement(0, 2) )) {
+            try {
+                seq.map(l -> l, e);
+                fail();
+            } catch (IllegalArgumentException ex) {
+                assertTrue(true);
+            }
+        }
+    }
+
+    @Test
+    public void testStructPaths() {
+        long[] offsets = { 0, 8, 24, 56 };
+        GroupLayout g = MemoryLayout.ofStruct(
+                MemoryLayouts.JAVA_BYTE.withName("1"),
+                MemoryLayouts.JAVA_CHAR.withName("2"),
+                MemoryLayouts.JAVA_FLOAT.withName("3"),
+                MemoryLayouts.JAVA_LONG.withName("4")
+        );
+
+        // test select
+
+        for (int i = 1 ; i <= 4 ; i++) {
+            MemoryLayout selected = g.select(PathElement.groupElement(String.valueOf(i)));
+            assertTrue(selected == g.memberLayouts().get(i - 1));
+        }
+
+        // test offset
+
+        for (int i = 1 ; i <= 4 ; i++) {
+            long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
+            assertEquals(offsets[i - 1], offset);
+        }
+
+        // test map
+
+        for (int i = 1 ; i <= 4 ; i++) {
+            GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, PathElement.groupElement(String.valueOf(i)));
+            assertTrue(g2.isStruct());
+            for (int j = 0 ; j < 4 ; j++) {
+                if (j == i - 1) {
+                    assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE);
+                } else {
+                    assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testUnionPaths() {
+        long[] offsets = { 0, 0, 0, 0 };
+        GroupLayout g = MemoryLayout.ofUnion(
+                MemoryLayouts.JAVA_BYTE.withName("1"),
+                MemoryLayouts.JAVA_CHAR.withName("2"),
+                MemoryLayouts.JAVA_FLOAT.withName("3"),
+                MemoryLayouts.JAVA_LONG.withName("4")
+        );
+
+        // test select
+
+        for (int i = 1 ; i <= 4 ; i++) {
+            MemoryLayout selected = g.select(PathElement.groupElement(String.valueOf(i)));
+            assertTrue(selected == g.memberLayouts().get(i - 1));
+        }
+
+        // test offset
+
+        for (int i = 1 ; i <= 4 ; i++) {
+            long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
+            assertEquals(offsets[i - 1], offset);
+        }
+
+        // test map
+
+        for (int i = 1 ; i <= 4 ; i++) {
+            GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, PathElement.groupElement(String.valueOf(i)));
+            assertTrue(g2.isUnion());
+            for (int j = 0 ; j < 4 ; j++) {
+                if (j == i - 1) {
+                    assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE);
+                } else {
+                    assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testSequencePaths() {
+        long[] offsets = { 0, 8, 16, 24 };
+        SequenceLayout g = MemoryLayout.ofSequence(4, MemoryLayouts.JAVA_BYTE);
+
+        // test select
+
+        MemoryLayout selected = g.select(PathElement.sequenceElement());
+        assertTrue(selected == MemoryLayouts.JAVA_BYTE);
+
+        // test offset
+
+        for (int i = 0 ; i < 4 ; i++) {
+            long offset = g.offset(PathElement.sequenceElement(i));
+            assertEquals(offsets[i], offset);
+        }
+
+        // test map
+
+        SequenceLayout seq2 = (SequenceLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, PathElement.sequenceElement());
+        assertTrue(seq2.elementLayout() == MemoryLayouts.JAVA_DOUBLE);
+    }
 }
 
--- a/test/jdk/java/foreign/TestLayouts.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/test/jdk/java/foreign/TestLayouts.java	Wed Jan 15 11:09:00 2020 +0000
@@ -34,6 +34,7 @@
 import java.util.function.LongFunction;
 
 import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.SequenceLayout;
 import org.testng.annotations.*;
 import static org.testng.Assert.*;
 
@@ -55,11 +56,13 @@
                 MemoryLayouts.JAVA_INT.withName("size"),
                 MemoryLayout.ofPaddingBits(32),
                 MemoryLayout.ofSequence(MemoryLayouts.JAVA_DOUBLE).withName("arr"));
+        assertFalse(layout.hasSize());
         VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
         VarHandle array_elem_handle = layout.varHandle(double.class,
                 MemoryLayout.PathElement.groupElement("arr"),
                 MemoryLayout.PathElement.sequenceElement());
-        try (MemorySegment segment = MemorySegment.allocateNative(8 + 8 * 4)) {
+        try (MemorySegment segment = MemorySegment.allocateNative(
+                layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr")))) {
             size_handle.set(segment.baseAddress(), 4);
             for (int i = 0 ; i < 4 ; i++) {
                 array_elem_handle.set(segment.baseAddress(), i, (double)i);
@@ -78,12 +81,14 @@
                 MemoryLayouts.JAVA_INT.withName("size"),
                 MemoryLayout.ofPaddingBits(32),
                 MemoryLayout.ofSequence(1, MemoryLayout.ofSequence(MemoryLayouts.JAVA_DOUBLE)).withName("arr"));
+        assertFalse(layout.hasSize());
         VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
         VarHandle array_elem_handle = layout.varHandle(double.class,
                 MemoryLayout.PathElement.groupElement("arr"),
                 MemoryLayout.PathElement.sequenceElement(0),
                 MemoryLayout.PathElement.sequenceElement());
-        try (MemorySegment segment = MemorySegment.allocateNative(8 + 8 * 4)) {
+        try (MemorySegment segment = MemorySegment.allocateNative(
+                layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.sequenceElement()))) {
             size_handle.set(segment.baseAddress(), 4);
             for (int i = 0 ; i < 4 ; i++) {
                 array_elem_handle.set(segment.baseAddress(), i, (double)i);
@@ -135,6 +140,18 @@
         layout.hashCode();
     }
 
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testBadUnboundSequenceLayoutResize() {
+        SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
+        seq.withElementCount(-1);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testBadBoundSequenceLayoutResize() {
+        SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
+        seq.withElementCount(-1);
+    }
+
     @Test
     public void testEmptyGroup() {
         MemoryLayout struct = MemoryLayout.ofStruct();
--- a/test/jdk/java/foreign/TestMemoryAccess.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/test/jdk/java/foreign/TestMemoryAccess.java	Wed Jan 15 11:09:00 2020 +0000
@@ -94,7 +94,7 @@
                 return;
             }
             try {
-                checker.check(handle, addr.offset(layout.byteSize()));
+                checker.check(handle, addr.addOffset(layout.byteSize()));
                 throw new AssertionError(); //not ok, out of bounds
             } catch (IndexOutOfBoundsException ex) {
                 //ok, should fail (out of bounds)
--- a/test/jdk/java/foreign/TestMemoryAlignment.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/test/jdk/java/foreign/TestMemoryAlignment.java	Wed Jan 15 11:09:00 2020 +0000
@@ -69,7 +69,7 @@
         VarHandle vh = aligned.varHandle(int.class);
         try (MemorySegment segment = MemorySegment.allocateNative(alignedGroup)) {
             MemoryAddress addr = segment.baseAddress();
-            vh.set(addr.offset(1L), -42);
+            vh.set(addr.addOffset(1L), -42);
             assertEquals(align, 8); //this is the only case where access is aligned
         } catch (IllegalStateException ex) {
             assertNotEquals(align, 8); //if align != 8, access is always unaligned
--- a/test/jdk/java/foreign/TestMemoryCopy.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/test/jdk/java/foreign/TestMemoryCopy.java	Wed Jan 15 11:09:00 2020 +0000
@@ -28,7 +28,6 @@
  */
 
 import jdk.incubator.foreign.MemoryAddress;
-import jdk.incubator.foreign.MemoryHandles;
 import jdk.incubator.foreign.MemoryLayouts;
 import jdk.incubator.foreign.MemorySegment;
 import org.testng.annotations.DataProvider;
@@ -36,11 +35,8 @@
 
 import java.lang.invoke.VarHandle;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.function.IntFunction;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
 
 import static org.testng.Assert.*;
 
@@ -55,16 +51,16 @@
         int size = Math.min(s1.size(), s2.size());
         //prepare source and target segments
         for (int i = 0 ; i < size ; i++) {
-            BYTE_HANDLE.set(addr2.offset(i), (byte)0);
+            BYTE_HANDLE.set(addr2.addOffset(i), (byte)0);
         }
         for (int i = 0 ; i < size ; i++) {
-            BYTE_HANDLE.set(addr1.offset(i), (byte) i);
+            BYTE_HANDLE.set(addr1.addOffset(i), (byte) i);
         }
         //perform copy
         MemoryAddress.copy(addr1, addr2, size);
         //check that copy actually worked
         for (int i = 0 ; i < size ; i++) {
-            assertEquals((byte)i, BYTE_HANDLE.get(addr2.offset(i)));
+            assertEquals((byte)i, BYTE_HANDLE.get(addr2.addOffset(i)));
         }
     }
 
--- a/test/jdk/java/foreign/TestSegments.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/test/jdk/java/foreign/TestSegments.java	Wed Jan 15 11:09:00 2020 +0000
@@ -26,11 +26,11 @@
  * @run testng TestSegments
  */
 
+import jdk.incubator.foreign.MemoryAddress;
 import jdk.incubator.foreign.MemoryLayout;
 import jdk.incubator.foreign.MemoryLayouts;
 import jdk.incubator.foreign.MemorySegment;
 
-import java.awt.font.LayoutPath;
 import java.lang.invoke.VarHandle;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -41,7 +41,6 @@
 import java.util.function.LongFunction;
 import java.util.stream.Stream;
 
-import jdk.incubator.foreign.SequenceLayout;
 import org.testng.annotations.*;
 
 import static org.testng.Assert.*;
@@ -96,6 +95,32 @@
         }
     }
 
+    @Test
+    public void testSlices() {
+        VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
+                .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
+        try (MemorySegment segment = MemorySegment.allocateNative(10)) {
+            //init
+            for (byte i = 0 ; i < segment.byteSize() ; i++) {
+                byteHandle.set(segment.baseAddress(), (long)i, i);
+            }
+            long start = 0;
+            MemoryAddress base = segment.baseAddress();
+            MemoryAddress last = base.addOffset(10);
+            while (!base.equals(last)) {
+                MemorySegment slice = segment.asSlice(base.offset(), 10 - start);
+                for (long i = start ; i < 10 ; i++) {
+                    assertEquals(
+                            byteHandle.get(segment.baseAddress(), i),
+                            byteHandle.get(slice.baseAddress(), i - start)
+                    );
+                }
+                base = base.addOffset(1);
+                start++;
+            }
+        }
+    }
+
     @DataProvider(name = "badSizeAndAlignments")
     public Object[][] sizesAndAlignments() {
         return new Object[][] {
--- a/test/jdk/java/foreign/TestVarHandleCombinators.java	Wed Jan 15 11:31:16 2020 +0100
+++ b/test/jdk/java/foreign/TestVarHandleCombinators.java	Wed Jan 15 11:09:00 2020 +0000
@@ -162,7 +162,7 @@
                 for (long j = 0; j < inner_size; j++) {
                     outer_vh.set(segment.baseAddress(), i, j, count);
                     assertEquals(
-                            (int)inner_vh.get(segment.baseAddress().offset(i * inner_size * 8), j),
+                            (int)inner_vh.get(segment.baseAddress().addOffset(i * inner_size * 8), j),
                             count);
                     count++;
                 }