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 ---