1 /*
   2  *  Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  *  This code is free software; you can redistribute it and/or modify it
   6  *  under the terms of the GNU General Public License version 2 only, as
   7  *  published by the Free Software Foundation.  Oracle designates this
   8  *  particular file as subject to the "Classpath" exception as provided
   9  *  by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  *  This code is distributed in the hope that it will be useful, but WITHOUT
  12  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  *  version 2 for more details (a copy is included in the LICENSE file that
  15  *  accompanied this code).
  16  *
  17  *  You should have received a copy of the GNU General Public License version
  18  *  2 along with this work; if not, write to the Free Software Foundation,
  19  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  *  or visit www.oracle.com if you need additional information or have any
  23  *  questions.
  24  *
  25  */
  26 package jdk.internal.foreign;
  27 
  28 import jdk.incubator.foreign.MemoryLayout;
  29 import jdk.internal.access.JavaLangInvokeAccess;
  30 import jdk.internal.access.SharedSecrets;
  31 import sun.invoke.util.Wrapper;
  32 
  33 import jdk.incubator.foreign.GroupLayout;
  34 import jdk.incubator.foreign.SequenceLayout;
  35 import jdk.incubator.foreign.ValueLayout;
  36 import java.lang.invoke.VarHandle;
  37 import java.util.ArrayList;
  38 import java.util.List;
  39 import java.util.function.UnaryOperator;
  40 import java.util.stream.LongStream;
  41 import java.util.stream.Stream;
  42 
  43 /**
  44  * This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)},
  45  * a path can be constructed by selecting layout elements using the selector methods provided by this class
  46  * (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}).
  47  * Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected
  48  * by the path (see {@link #offset}), or obtain a memory access var handle to access the selected layout element
  49  * given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle(Class)}).
  50  */
  51 public class LayoutPath {
  52 
  53     private static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
  54 
  55     private final MemoryLayout layout;
  56     private final long offset;
  57     private final LayoutPath enclosing;
  58     private final long[] scales;
  59 
  60     private LayoutPath(MemoryLayout layout, long offset, long[] scales, LayoutPath enclosing) {
  61         this.layout = layout;
  62         this.offset = offset;
  63         this.scales = scales;
  64         this.enclosing = enclosing;
  65     }
  66 
  67     // Layout path selector methods
  68 
  69     public LayoutPath sequenceElement() {
  70         check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
  71         SequenceLayout seq = (SequenceLayout)layout;
  72         MemoryLayout elem = seq.elementLayout();
  73         return LayoutPath.nestedPath(elem, offset, addScale(elem.bitSize()), this);
  74     }
  75 
  76     public LayoutPath sequenceElement(long start, long step) {
  77         check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
  78         SequenceLayout seq = (SequenceLayout)layout;
  79         checkSequenceBounds(seq, start);
  80         MemoryLayout elem = seq.elementLayout();
  81         long elemSize = elem.bitSize();
  82         return LayoutPath.nestedPath(elem, offset + (start * elemSize), addScale(elemSize * step), this);
  83     }
  84 
  85     public LayoutPath sequenceElement(long index) {
  86         check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
  87         SequenceLayout seq = (SequenceLayout)layout;
  88         checkSequenceBounds(seq, index);
  89         return LayoutPath.nestedPath(seq.elementLayout(), offset, scales, this);
  90     }
  91 
  92     public LayoutPath groupElement(String name) {
  93         check(GroupLayout.class, "attempting to select a group element from a non-group layout");
  94         GroupLayout g = (GroupLayout)layout;
  95         long offset = 0;
  96         MemoryLayout elem = null;
  97         for (int i = 0; i < g.memberLayouts().size(); i++) {
  98             MemoryLayout l = g.memberLayouts().get(i);
  99             if (l.name().isPresent() &&
 100                 l.name().get().equals(name)) {
 101                 elem = l;
 102                 break;
 103             } else {
 104                 offset += l.bitSize();
 105             }
 106         }
 107         if (elem == null) {
 108             throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
 109         }
 110         return LayoutPath.nestedPath(elem, this.offset + offset, scales, this);
 111     }
 112 
 113     // Layout path projections
 114 
 115     public long offset() {
 116         return offset;
 117     }
 118 
 119     public VarHandle dereferenceHandle(Class<?> carrier) {
 120         if (!(layout instanceof ValueLayout)) {
 121             throw badLayoutPath("layout path does not select a value layout");
 122         }
 123 
 124         if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class // illegal carrier?
 125                 || Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize()) { // carrier has the right size?
 126             throw new IllegalArgumentException("Invalid carrier: " + carrier + ", for layout " + layout);
 127         }
 128 
 129         checkAlignment(this);
 130 
 131         return JLI.memoryAddressViewVarHandle(
 132                 carrier,
 133                 layout.byteAlignment() - 1, //mask
 134                 ((ValueLayout) layout).order(),
 135                 Utils.bitsToBytesOrThrow(offset, IllegalStateException::new),
 136                 LongStream.of(scales).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray());
 137     }
 138 
 139     // Layout path construction
 140 
 141     public static LayoutPath rootPath(MemoryLayout layout) {
 142         return new LayoutPath(layout, 0L, EMPTY_SCALES, null);
 143     }
 144 
 145     private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] scales, LayoutPath encl) {
 146         return new LayoutPath(layout, offset, scales, encl);
 147     }
 148 
 149     // Helper methods
 150 
 151     private void check(Class<?> layoutClass, String msg) {
 152         if (!layoutClass.isAssignableFrom(layout.getClass())) {
 153             throw badLayoutPath(msg);
 154         }
 155     }
 156 
 157     private void checkSequenceBounds(SequenceLayout seq, long index) {
 158         if (seq.elementCount().isPresent() && index >= seq.elementCount().getAsLong()) {
 159             throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount().getAsLong()));
 160         }
 161     }
 162 
 163     private static IllegalArgumentException badLayoutPath(String cause) {
 164         return new IllegalArgumentException("Bad layout path: " + cause);
 165     }
 166 
 167     private static void checkAlignment(LayoutPath path) {
 168         MemoryLayout layout = path.layout;
 169         long alignment = layout.bitAlignment();
 170         if (path.offset % alignment != 0) {
 171             throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout);
 172         }
 173         LayoutPath encl = path.enclosing;
 174         if (encl != null) {
 175             if (encl.layout.bitAlignment() < alignment) {
 176                 throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout);
 177             }
 178             checkAlignment(encl);
 179         }
 180     }
 181 
 182     private long[] addScale(long scale) {
 183         long[] newScales = new long[scales.length + 1];
 184         System.arraycopy(scales, 0, newScales, 0, scales.length);
 185         newScales[scales.length] = scale;
 186         return newScales;
 187     }
 188 
 189     private static long[] EMPTY_SCALES = new long[0];
 190 
 191     /**
 192      * This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
 193      * is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
 194      */
 195     public static class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
 196 
 197         final UnaryOperator<LayoutPath> pathOp;
 198 
 199         public PathElementImpl(UnaryOperator<LayoutPath> pathOp) {
 200             this.pathOp = pathOp;
 201         }
 202 
 203         @Override
 204         public LayoutPath apply(LayoutPath layoutPath) {
 205             return pathOp.apply(layoutPath);
 206         }
 207     }
 208 }
--- EOF ---