1 /*
  2  * Copyright (c) 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 package jdk.internal.jextract.impl;
 26 
 27 import jdk.incubator.foreign.GroupLayout;
 28 import jdk.incubator.foreign.MemoryLayout;
 29 import jdk.incubator.foreign.MemorySegment;
 30 import jdk.incubator.jextract.Declaration;
 31 import jdk.incubator.jextract.Type;
 32 
 33 import java.util.ArrayDeque;
 34 import java.util.ArrayList;
 35 import java.util.Collections;
 36 import java.util.Deque;
 37 import java.util.List;
 38 
 39 /**
 40  * This class generates static utilities class for C structs, unions.
 41  */
 42 class StructBuilder extends ConstantBuilder {
 43 
 44     private static final String MEMBER_MODS = "public static";
 45 
 46     private final GroupLayout structLayout;
 47     private final Type structType;
 48     private final Deque<String> prefixElementNames;
 49 
 50     StructBuilder(JavaSourceBuilder enclosing, String name, GroupLayout structLayout, Type structType) {
 51         super(enclosing, name);
 52         this.structLayout = structLayout;
 53         this.structType = structType;
 54         prefixElementNames = new ArrayDeque<>();
 55     }
 56 
 57     private String safeParameterName(String paramName) {
 58         return isEnclosedBySameName(paramName)? paramName + "$" : paramName;
 59     }
 60 
 61     void pushPrefixElement(String prefixElementName) {
 62         prefixElementNames.push(prefixElementName);
 63     }
 64 
 65     void popPrefixElement() {
 66         prefixElementNames.pop();
 67     }
 68 
 69     private List<String> prefixNamesList() {
 70         List<String> prefixes = new ArrayList<>(prefixElementNames);
 71         Collections.reverse(prefixes);
 72         return Collections.unmodifiableList(prefixes);
 73     }
 74 
 75     @Override
 76     void classBegin() {
 77         if (!inAnonymousNested()) {
 78             super.classBegin();
 79             addLayout(layoutField(), ((Type.Declared) structType).tree().layout().get())
 80                     .emitGetter(this, MEMBER_MODS, Constant.SUFFIX_ONLY);
 81         }
 82     }
 83 
 84     @Override
 85     JavaSourceBuilder classEnd() {
 86         if (!inAnonymousNested()) {
 87             emitSizeof();
 88             emitAllocatorAllocate();
 89             emitAllocatorAllocateArray();
 90             emitScopeAllocate();
 91             emitScopeAllocateArray();
 92             emitOfAddressScoped();
 93             return super.classEnd();
 94         } else {
 95             // we're in an anonymous struct which got merged into this one, return this very builder and keep it open
 96             popPrefixElement();
 97             return this;
 98         }
 99     }
100 
101     boolean inAnonymousNested() {
102         return !prefixElementNames.isEmpty();
103     }
104 
105     @Override
106     public StructBuilder addStruct(String name, Declaration parent, GroupLayout layout, Type type) {
107         if (name.isEmpty() && (parent instanceof Declaration.Scoped)) {
108             //nested anon struct - merge into this builder!
109             String anonName = layout.name().orElseThrow();
110             pushPrefixElement(anonName);
111             return this;
112         } else {
113             return new StructBuilder(this, name.isEmpty() ? parent.name() : name, layout, type);
114         }
115     }
116 
117     @Override
118     public String addFunctionalInterface(String name, FunctionInfo functionInfo) {
119         FunctionalInterfaceBuilder builder = new FunctionalInterfaceBuilder(this, name, functionInfo.methodType(), functionInfo.descriptor());
120         builder.classBegin();
121         builder.classEnd();
122         return builder.className();
123     }
124 
125     @Override
126     public void addVar(String javaName, String nativeName, VarInfo varInfo) {
127         try {
128             structLayout.byteOffset(elementPaths(nativeName));
129         } catch (UnsupportedOperationException uoe) {
130             // bad layout - do nothing
131             OutputFactory.warn("skipping '" + className() + "." + nativeName + "' : " + uoe.toString());
132             return;
133         }
134         if (varInfo.carrier().equals(MemorySegment.class)) {
135             emitSegmentGetter(javaName, nativeName, varInfo.layout());
136         } else {
137             Constant vhConstant = addFieldVarHandle(javaName, nativeName, varInfo, layoutField(), prefixNamesList())
138                     .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME);
139             emitFieldGetter(vhConstant, javaName, varInfo.carrier());
140             emitFieldSetter(vhConstant, javaName, varInfo.carrier());
141             emitIndexedFieldGetter(vhConstant, javaName, varInfo.carrier());
142             emitIndexedFieldSetter(vhConstant, javaName, varInfo.carrier());
143             if (varInfo.fiName().isPresent()) {
144                 emitFunctionalInterfaceGetter(varInfo.fiName().get(), javaName);
145             }
146         }
147     }
148 
149     private void emitFunctionalInterfaceGetter(String fiName, String javaName) {
150         incrAlign();
151         indent();
152         append(MEMBER_MODS + " ");
153         append(fiName + " " + javaName + " (MemorySegment segment, ResourceScope scope) {\n");
154         incrAlign();
155         indent();
156         append("return " + fiName + ".ofAddress(" + javaName + "$get(segment), scope);\n");
157         decrAlign();
158         indent();
159         append("}\n");
160         decrAlign();
161     }
162 
163     private void emitFieldGetter(Constant vhConstant, String javaName, Class<?> type) {
164         incrAlign();
165         indent();
166         String seg = safeParameterName("seg");
167         append(MEMBER_MODS + " " + type.getSimpleName() + " " + javaName + "$get(MemorySegment " + seg + ") {\n");
168         incrAlign();
169         indent();
170         append("return (" + type.getName() + ")"
171                 + vhConstant.accessExpression() + ".get(" + seg + ");\n");
172         decrAlign();
173         indent();
174         append("}\n");
175         decrAlign();
176     }
177 
178     private void emitFieldSetter(Constant vhConstant, String javaName, Class<?> type) {
179         incrAlign();
180         indent();
181         String seg = safeParameterName("seg");
182         String x = safeParameterName("x");
183         String param = MemorySegment.class.getSimpleName() + " " + seg;
184         append(MEMBER_MODS + " void " + javaName + "$set( " + param + ", " + type.getSimpleName() + " " + x + ") {\n");
185         incrAlign();
186         indent();
187         append(vhConstant.accessExpression() + ".set(" + seg + ", " + x + ");\n");
188         decrAlign();
189         indent();
190         append("}\n");
191         decrAlign();
192     }
193 
194     private MemoryLayout.PathElement[] elementPaths(String nativeFieldName) {
195         List<String> prefixElements = prefixNamesList();
196         MemoryLayout.PathElement[] elems = new MemoryLayout.PathElement[prefixElements.size() + 1];
197         int i = 0;
198         for (; i < prefixElements.size(); i++) {
199             elems[i] = MemoryLayout.PathElement.groupElement(prefixElements.get(i));
200         }
201         elems[i] = MemoryLayout.PathElement.groupElement(nativeFieldName);
202         return elems;
203     }
204 
205     private void emitSegmentGetter(String javaName, String nativeName, MemoryLayout layout) {
206         incrAlign();
207         indent();
208         String seg = safeParameterName("seg");
209         append(MEMBER_MODS + " MemorySegment " + javaName + "$slice(MemorySegment " + seg + ") {\n");
210         incrAlign();
211         indent();
212         append("return " + seg + ".asSlice(");
213         append(structLayout.byteOffset(elementPaths(nativeName)));
214         append(", ");
215         append(layout.byteSize());
216         append(");\n");
217         decrAlign();
218         indent();
219         append("}\n");
220         decrAlign();
221     }
222 
223     private void emitSizeof() {
224         incrAlign();
225         indent();
226         append(MEMBER_MODS);
227         append(" long sizeof() { return $LAYOUT().byteSize(); }\n");
228         decrAlign();
229     }
230 
231     private void emitAllocatorAllocate() {
232         incrAlign();
233         indent();
234         append(MEMBER_MODS);
235         append(" MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); }\n");
236         decrAlign();
237     }
238 
239     private void emitAllocatorAllocateArray() {
240         incrAlign();
241         indent();
242         append(MEMBER_MODS);
243         append(" MemorySegment allocateArray(int len, SegmentAllocator allocator) {\n");
244         incrAlign();
245         indent();
246         append("return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT()));\n");
247         decrAlign();
248         indent();
249         append("}\n");
250         decrAlign();
251     }
252 
253     private void emitScopeAllocate() {
254         incrAlign();
255         indent();
256         append(MEMBER_MODS);
257         append(" MemorySegment allocate(ResourceScope scope) { return allocate(SegmentAllocator.nativeAllocator(scope)); }\n");
258         decrAlign();
259     }
260 
261     private void emitScopeAllocateArray() {
262         incrAlign();
263         indent();
264         append(MEMBER_MODS);
265         append(" MemorySegment allocateArray(int len, ResourceScope scope) {\n");
266         incrAlign();
267         indent();
268         append("return allocateArray(len, SegmentAllocator.nativeAllocator(scope));\n");
269         decrAlign();
270         indent();
271         append("}\n");
272         decrAlign();
273     }
274 
275     private void emitOfAddressScoped() {
276         incrAlign();
277         indent();
278         append(MEMBER_MODS);
279         append(" MemorySegment ofAddress(MemoryAddress addr, ResourceScope scope) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, scope); }\n");
280         decrAlign();
281     }
282 
283     private void emitIndexedFieldGetter(Constant vhConstant, String javaName, Class<?> type) {
284         incrAlign();
285         indent();
286         String index = safeParameterName("index");
287         String seg = safeParameterName("seg");
288         String params = MemorySegment.class.getSimpleName() + " " + seg + ", long " + index;
289         append(MEMBER_MODS + " " + type.getSimpleName() + " " + javaName + "$get(" + params + ") {\n");
290         incrAlign();
291         indent();
292         append("return (" + type.getName() + ")");
293         append(vhConstant.accessExpression());
294         append(".get(");
295         append(seg);
296         append(".asSlice(");
297         append(index);
298         append("*sizeof()));\n");
299         decrAlign();
300         indent();
301         append("}\n");
302         decrAlign();
303     }
304 
305     private void emitIndexedFieldSetter(Constant vhConstant, String javaName, Class<?> type) {
306         incrAlign();
307         indent();
308         String index = safeParameterName("index");
309         String seg = safeParameterName("seg");
310         String x = safeParameterName("x");
311         String params = MemorySegment.class.getSimpleName() + " " + seg +
312             ", long " + index + ", " + type.getSimpleName() + " " + x;
313         append(MEMBER_MODS + " void " + javaName + "$set(" + params + ") {\n");
314         incrAlign();
315         indent();
316         append(vhConstant.accessExpression());
317         append(".set(");
318         append(seg);
319         append(".asSlice(");
320         append(index);
321         append("*sizeof()), ");
322         append(x);
323         append(");\n");
324         decrAlign();
325         indent();
326         append("}\n");
327         decrAlign();
328     }
329 
330     private String qualifiedName(ClassSourceBuilder builder) {
331         if (builder.isNested()) {
332             String prefix = qualifiedName((ClassSourceBuilder)builder.enclosing);
333             return prefix.isEmpty() ?
334                     builder.className() :
335                     prefix + "$" + builder.className();
336         } else {
337             return "";
338         }
339     }
340 
341     private String layoutField() {
342         String suffix = structLayout.isUnion() ? "union" : "struct";
343         return qualifiedName(this) + "$" + suffix;
344     }
345 }