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 }