1 /*
  2  *  Copyright (c) 2019, 2020, 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.incubator.foreign;
 27 
 28 import java.lang.constant.ClassDesc;
 29 import java.lang.constant.Constable;
 30 import java.lang.constant.ConstantDesc;
 31 import java.lang.constant.ConstantDescs;
 32 import java.lang.constant.DirectMethodHandleDesc;
 33 import java.lang.constant.DynamicConstantDesc;
 34 import java.lang.constant.MethodHandleDesc;
 35 import java.lang.constant.MethodTypeDesc;
 36 import java.nio.ByteOrder;
 37 import java.util.Collections;
 38 import java.util.HashMap;
 39 import java.util.Map;
 40 import java.util.Objects;
 41 import java.util.Optional;
 42 import java.util.OptionalLong;
 43 import java.util.stream.Collectors;
 44 import java.util.stream.Stream;
 45 
 46 import static java.lang.constant.ConstantDescs.BSM_GET_STATIC_FINAL;
 47 import static java.lang.constant.ConstantDescs.BSM_INVOKE;
 48 import static java.lang.constant.ConstantDescs.CD_String;
 49 import static java.lang.constant.ConstantDescs.CD_long;
 50 
 51 abstract non-sealed class AbstractLayout implements MemoryLayout {
 52 
 53     private final OptionalLong size;
 54     final long alignment;
 55     final Map<String, Constable> attributes;
 56 
 57     public AbstractLayout(OptionalLong size, long alignment, Map<String, Constable> attributes) {
 58         this.size = size;
 59         this.alignment = alignment;
 60         this.attributes = Collections.unmodifiableMap(attributes);
 61     }
 62 
 63     @Override
 64     public AbstractLayout withName(String name) {
 65         Objects.requireNonNull(name);
 66         return withAttribute(LAYOUT_NAME, name);
 67     }
 68 
 69     @Override
 70     public final Optional<String> name() {
 71         return attribute(LAYOUT_NAME).map(String.class::cast);
 72     }
 73 
 74     @Override
 75     public Optional<Constable> attribute(String name) {
 76         Objects.requireNonNull(name);
 77         return Optional.ofNullable(attributes.get(name));
 78     }
 79 
 80     @Override
 81     public Stream<String> attributes() {
 82         return attributes.keySet().stream();
 83     }
 84 
 85     @Override
 86     public AbstractLayout withAttribute(String name, Constable value) {
 87         Objects.requireNonNull(name);
 88         Map<String, Constable> newAttributes = new HashMap<>(attributes);
 89         newAttributes.put(name, value);
 90         return dup(alignment, newAttributes);
 91     }
 92 
 93     abstract AbstractLayout dup(long alignment, Map<String, Constable> annos);
 94 
 95     @Override
 96     public AbstractLayout withBitAlignment(long alignmentBits) {
 97         checkAlignment(alignmentBits);
 98         return dup(alignmentBits, attributes);
 99     }
100 
101     void checkAlignment(long alignmentBitCount) {
102         if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two
103                 (alignmentBitCount < 8)) { //alignment must be greater than 8
104             throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount);
105         }
106     }
107 
108     static void checkSize(long size) {
109         checkSize(size, false);
110     }
111 
112     static void checkSize(long size, boolean includeZero) {
113         if (size < 0 || (!includeZero && size == 0)) {
114             throw new IllegalArgumentException("Invalid size for layout: " + size);
115         }
116     }
117 
118     @Override
119     public final long bitAlignment() {
120         return alignment;
121     }
122 
123     @Override
124     public boolean hasSize() {
125         return size.isPresent();
126     }
127 
128     @Override
129     public long bitSize() {
130         return size.orElseThrow(AbstractLayout::badSizeException);
131     }
132 
133     static OptionalLong optSize(MemoryLayout layout) {
134         return ((AbstractLayout)layout).size;
135     }
136 
137     private static UnsupportedOperationException badSizeException() {
138         return new UnsupportedOperationException("Cannot compute size of a layout which is, or depends on a sequence layout with unspecified size");
139     }
140 
141     String decorateLayoutString(String s) {
142         if (name().isPresent()) {
143             s = String.format("%s(%s)", s, name().get());
144         }
145         if (!hasNaturalAlignment()) {
146             s = alignment + "%" + s;
147         }
148         if (!attributes.isEmpty()) {
149             s += attributes.entrySet().stream()
150                                       .map(e -> e.getKey() + "=" + e.getValue())
151                                       .collect(Collectors.joining(",", "[", "]"));
152         }
153         return s;
154     }
155 
156     <T> DynamicConstantDesc<T> decorateLayoutConstant(DynamicConstantDesc<T> desc) {
157         if (!hasNaturalAlignment()) {
158             desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withBitAlignment", desc.constantType(), MH_WITH_BIT_ALIGNMENT,
159                     desc, bitAlignment());
160         }
161         for (var e : attributes.entrySet()) {
162             desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withAttribute", desc.constantType(), MH_WITH_ATTRIBUTE,
163                     desc, e.getKey(), e.getValue().describeConstable().orElseThrow());
164         }
165 
166         return desc;
167     }
168 
169     boolean hasNaturalAlignment() {
170         return size.isPresent() && size.getAsLong() == alignment;
171     }
172 
173     @Override
174     public boolean isPadding() {
175         return this instanceof PaddingLayout;
176     }
177 
178     @Override
179     public int hashCode() {
180         return attributes.hashCode() << Long.hashCode(alignment);
181     }
182 
183     @Override
184     public boolean equals(Object other) {
185         if (this == other) {
186             return true;
187         }
188 
189         if (!(other instanceof AbstractLayout)) {
190             return false;
191         }
192 
193         return Objects.equals(attributes, ((AbstractLayout) other).attributes) &&
194                 Objects.equals(alignment, ((AbstractLayout) other).alignment);
195     }
196 
197     /*** Helper constants for implementing Layout::describeConstable ***/
198 
199     static final ClassDesc CD_MEMORY_LAYOUT = MemoryLayout.class.describeConstable().get();
200 
201     static final ClassDesc CD_VALUE_LAYOUT = ValueLayout.class.describeConstable().get();
202 
203     static final ClassDesc CD_SEQUENCE_LAYOUT = SequenceLayout.class.describeConstable().get();
204 
205     static final ClassDesc CD_GROUP_LAYOUT = GroupLayout.class.describeConstable().get();
206 
207     static final ClassDesc CD_BYTEORDER = ByteOrder.class.describeConstable().get();
208 
209     static final ClassDesc CD_FUNCTION_DESC = FunctionDescriptor.class.describeConstable().get();
210 
211     static final ClassDesc CD_Constable = Constable.class.describeConstable().get();
212 
213     static final ConstantDesc BIG_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "BIG_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
214 
215     static final ConstantDesc LITTLE_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "LITTLE_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
216 
217     static final MethodHandleDesc MH_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "paddingLayout",
218                 MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long));
219 
220     static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "valueLayout",
221                 MethodTypeDesc.of(CD_VALUE_LAYOUT, CD_long, CD_BYTEORDER));
222 
223     static final MethodHandleDesc MH_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "sequenceLayout",
224                 MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_long, CD_MEMORY_LAYOUT));
225 
226     static final MethodHandleDesc MH_UNSIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "sequenceLayout",
227                 MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_MEMORY_LAYOUT));
228 
229     static final MethodHandleDesc MH_STRUCT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "structLayout",
230                 MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_MEMORY_LAYOUT.arrayType()));
231 
232     static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "unionLayout",
233                 MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_MEMORY_LAYOUT.arrayType()));
234 
235     static final MethodHandleDesc MH_VOID_FUNCTION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, CD_FUNCTION_DESC, "ofVoid",
236                 MethodTypeDesc.of(CD_FUNCTION_DESC, CD_MEMORY_LAYOUT.arrayType()));
237 
238     static final MethodHandleDesc MH_FUNCTION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, CD_FUNCTION_DESC, "of",
239                 MethodTypeDesc.of(CD_FUNCTION_DESC, CD_MEMORY_LAYOUT, CD_MEMORY_LAYOUT.arrayType()));
240 
241     static final MethodHandleDesc MH_WITH_BIT_ALIGNMENT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withBitAlignment",
242                 MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long));
243 
244     static final MethodHandleDesc MH_WITH_ATTRIBUTE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withAttribute",
245                 MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String, CD_Constable));
246 }