1 /*
  2  * Copyright (c) 2021, 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.jextract.impl;
 27 
 28 import jdk.incubator.foreign.CLinker;
 29 import jdk.incubator.foreign.FunctionDescriptor;
 30 import jdk.incubator.foreign.GroupLayout;
 31 import jdk.incubator.foreign.MemoryAddress;
 32 import jdk.incubator.foreign.MemoryLayout;
 33 import jdk.incubator.foreign.MemorySegment;
 34 import jdk.incubator.foreign.SequenceLayout;
 35 import jdk.incubator.foreign.ValueLayout;
 36 
 37 import java.lang.invoke.MethodHandle;
 38 import java.lang.invoke.VarHandle;
 39 import java.util.HashMap;
 40 import java.util.List;
 41 import java.util.Map;
 42 import java.util.Objects;
 43 import java.util.function.Consumer;
 44 import java.util.function.Function;
 45 import java.util.function.Supplier;
 46 import java.util.stream.Collectors;
 47 
 48 public class ConstantBuilder extends ClassSourceBuilder {
 49 
 50     // set of names generates already
 51     private final Map<String, Constant> namesGenerated = new HashMap<>();
 52 
 53     public ConstantBuilder(JavaSourceBuilder enclosing, String className) {
 54         super(enclosing, Kind.CLASS, className);
 55     }
 56 
 57     String memberMods() {
 58         return kind == ClassSourceBuilder.Kind.CLASS ?
 59                 "static final " : "";
 60     }
 61 
 62     // public API
 63 
 64     public Constant addLayout(String javaName, MemoryLayout layout) {
 65         return emitIfAbsent(javaName, Constant.Kind.LAYOUT,
 66                 () -> emitLayoutField(javaName, layout));
 67     }
 68 
 69     public Constant addFieldVarHandle(String javaName, String nativeName, VarInfo varInfo,
 70                                       String rootJavaName, List<String> prefixElementNames) {
 71         return addVarHandle(javaName, nativeName, varInfo, rootJavaName, prefixElementNames);
 72     }
 73 
 74     public Constant addGlobalVarHandle(String javaName, String nativeName, VarInfo varInfo) {
 75         return addVarHandle(javaName, nativeName, varInfo, null, List.of());
 76     }
 77 
 78     private Constant addVarHandle(String javaName, String nativeName, VarInfo varInfo,
 79                                 String rootLayoutName, List<String> prefixElementNames) {
 80         return emitIfAbsent(javaName, Constant.Kind.VAR_HANDLE,
 81                 () -> emitVarHandleField(javaName, nativeName, varInfo, rootLayoutName, prefixElementNames));
 82     }
 83 
 84     public Constant addMethodHandle(String javaName, String nativeName, FunctionInfo functionInfo, boolean virtual) {
 85         return emitIfAbsent(javaName, Constant.Kind.METHOD_HANDLE,
 86                 () -> emitMethodHandleField(javaName, nativeName, functionInfo, virtual));
 87     }
 88 
 89     public Constant addSegment(String javaName, String nativeName, MemoryLayout layout) {
 90         return emitIfAbsent(javaName, Constant.Kind.SEGMENT,
 91                 () -> emitSegmentField(javaName, nativeName, layout));
 92     }
 93 
 94     public Constant addFunctionDesc(String javaName, FunctionDescriptor desc) {
 95         return emitIfAbsent(javaName, Constant.Kind.FUNCTION_DESCRIPTOR,
 96                 () -> emitFunctionDescField(javaName, desc));
 97     }
 98 
 99     public Constant addConstantDesc(String javaName, Class<?> type, Object value) {
100         if (type == MemorySegment.class) {
101             return emitIfAbsent(javaName, Constant.Kind.SEGMENT,
102                     () -> emitConstantSegment(javaName, value));
103         } else if (type == MemoryAddress.class) {
104             return emitIfAbsent(javaName, Constant.Kind.ADDRESS,
105                     () -> emitConstantAddress(javaName, value));
106         } else {
107             throw new UnsupportedOperationException();
108         }
109     }
110 
111     static class Constant {
112 
113         enum Kind {
114             LAYOUT(MemoryLayout.class, "$LAYOUT"),
115             METHOD_HANDLE(MethodHandle.class, "$MH"),
116             VAR_HANDLE(VarHandle.class, "$VH"),
117             FUNCTION_DESCRIPTOR(FunctionDescriptor.class, "$FUNC"),
118             ADDRESS(MemoryAddress.class, "$ADDR"),
119             SEGMENT(MemorySegment.class, "$SEGMENT");
120 
121             final Class<?> type;
122             final String nameSuffix;
123 
124             Kind(Class<?> type, String nameSuffix) {
125                 this.type = type;
126                 this.nameSuffix = nameSuffix;
127             }
128 
129             String fieldName(String javaName) {
130                 return javaName + nameSuffix;
131             }
132         }
133 
134         private final String className;
135         private final String javaName;
136         private final Kind kind;
137 
138         Constant(String className, String javaName, Kind kind) {
139             this.className = className;
140             this.javaName = javaName;
141             this.kind = kind;
142         }
143 
144         List<String> getterNameParts() {
145             return List.of(className, javaName, kind.nameSuffix);
146         }
147 
148         String accessExpression() {
149             return className + "." + kind.fieldName(javaName);
150         }
151 
152         Constant emitGetter(ClassSourceBuilder builder, String mods, Function<List<String>, String> getterNameFunc) {
153             builder.emitGetter(mods, kind.type, getterNameFunc.apply(getterNameParts()), accessExpression());
154             return this;
155         }
156 
157         Constant emitGetter(ClassSourceBuilder builder, String mods, Function<List<String>, String> getterNameFunc, String symbolName) {
158             builder.emitGetter(mods, kind.type, getterNameFunc.apply(getterNameParts()), accessExpression(), true, symbolName);
159             return this;
160         }
161 
162         static final Function<List<String>, String> QUALIFIED_NAME =
163                 l -> l.stream().skip(1).collect(Collectors.joining());
164 
165         static final Function<List<String>, String> JAVA_NAME =
166                 l -> l.get(1);
167 
168         static final Function<List<String>, String> SUFFIX_ONLY =
169                 l -> l.get(2);
170     }
171 
172     // private generators
173 
174     public Constant emitIfAbsent(String name, Constant.Kind kind, Supplier<Constant> constantFactory) {
175         String lookupName = kind.fieldName(name);
176         Constant constant = namesGenerated.get(lookupName);
177         if (constant == null) {
178             constant = constantFactory.get();
179             if (constant.kind != kind) {
180                 throw new AssertionError("Factory return wrong kind of constant; expected: "
181                         + kind + "; found: " + constant.kind);
182             }
183             namesGenerated.put(lookupName, constant);
184         }
185         return constant;
186     }
187 
188     private Constant emitMethodHandleField(String javaName, String nativeName, FunctionInfo functionInfo, boolean virtual) {
189         Constant functionDesc = addFunctionDesc(javaName, functionInfo.descriptor());
190         incrAlign();
191         String fieldName = Constant.Kind.METHOD_HANDLE.fieldName(javaName);
192         indent();
193         append(memberMods() + "MethodHandle ");
194         append(fieldName + " = RuntimeHelper.downcallHandle(\n");
195         incrAlign();
196         indent();
197         if (!virtual) {
198             append("\"" + nativeName + "\"");
199             append(",\n");
200             indent();
201         }
202         append(functionDesc.accessExpression());
203         append(", ");
204         // isVariadic
205         append(functionInfo.isVarargs());
206         append("\n");
207         decrAlign();
208         indent();
209         append(");\n");
210         decrAlign();
211         return new Constant(className(), javaName, Constant.Kind.METHOD_HANDLE);
212     }
213 
214     private Constant emitVarHandleField(String javaName, String nativeName, VarInfo varInfo,
215                                       String rootLayoutName, List<String> prefixElementNames) {
216         String layoutAccess = rootLayoutName != null ?
217                 Constant.Kind.LAYOUT.fieldName(rootLayoutName) :
218                 addLayout(javaName, varInfo.layout()).accessExpression();
219         incrAlign();
220         String typeName = varInfo.carrier().getName();
221         indent();
222         String fieldName = Constant.Kind.VAR_HANDLE.fieldName(javaName);
223         append(memberMods() + "VarHandle " + fieldName + " = ");
224         append(layoutAccess);
225         append(".varHandle(");
226         String prefix = "";
227         if (rootLayoutName != null) {
228             for (String prefixElementName : prefixElementNames) {
229                 append(prefix + "MemoryLayout.PathElement.groupElement(\"" + prefixElementName + "\")");
230                 prefix = ", ";
231             }
232             append(prefix + "MemoryLayout.PathElement.groupElement(\"" + nativeName + "\")");
233         }
234         append(")");
235         append(";\n");
236         decrAlign();
237         return new Constant(className(), javaName, Constant.Kind.VAR_HANDLE);
238     }
239 
240     private Constant emitLayoutField(String javaName, MemoryLayout layout) {
241         String fieldName = Constant.Kind.LAYOUT.fieldName(javaName);
242         incrAlign();
243         indent();
244         append(memberMods() + "MemoryLayout " + fieldName + " = ");
245         emitLayoutString(layout);
246         append(";\n");
247         decrAlign();
248         return new Constant(className(), javaName, Constant.Kind.LAYOUT);
249     }
250 
251     private void emitLayoutString(MemoryLayout l) {
252         if (l instanceof ValueLayout val) {
253             append(typeToLayoutName(val));
254         } else if (l instanceof SequenceLayout seq) {
255             append("MemoryLayout.sequenceLayout(");
256             if (seq.elementCount().isPresent()) {
257                 append(seq.elementCount().getAsLong() + ", ");
258             }
259             emitLayoutString(seq.elementLayout());
260             append(")");
261         } else if (l instanceof GroupLayout group) {
262             if (group.isStruct()) {
263                 append("MemoryLayout.structLayout(\n");
264             } else {
265                 append("MemoryLayout.unionLayout(\n");
266             }
267             incrAlign();
268             String delim = "";
269             for (MemoryLayout e : group.memberLayouts()) {
270                 append(delim);
271                 indent();
272                 emitLayoutString(e);
273                 delim = ",\n";
274             }
275             append("\n");
276             decrAlign();
277             indent();
278             append(")");
279         } else {
280             // padding (or unsupported)
281             append("MemoryLayout.paddingLayout(" + l.bitSize() + ")");
282         }
283         if (l.name().isPresent()) {
284             append(".withName(\"" +  l.name().get() + "\")");
285         }
286     }
287 
288     private Constant emitFunctionDescField(String javaName, FunctionDescriptor desc) {
289         incrAlign();
290         indent();
291         String fieldName = Constant.Kind.FUNCTION_DESCRIPTOR.fieldName(javaName);
292         final boolean noArgs = desc.argumentLayouts().isEmpty();
293         append(memberMods());
294         append("FunctionDescriptor ");
295         append(fieldName);
296         append(" = ");
297         if (desc.returnLayout().isPresent()) {
298             append("FunctionDescriptor.of(");
299             emitLayoutString(desc.returnLayout().get());
300             if (!noArgs) {
301                 append(",");
302             }
303         } else {
304             append("FunctionDescriptor.ofVoid(");
305         }
306         if (!noArgs) {
307             append("\n");
308             incrAlign();
309             String delim = "";
310             for (MemoryLayout e : desc.argumentLayouts()) {
311                 append(delim);
312                 indent();
313                 emitLayoutString(e);
314                 delim = ",\n";
315             }
316             append("\n");
317             decrAlign();
318             indent();
319         }
320         append(");\n");
321         decrAlign();
322         return new Constant(className(), javaName, Constant.Kind.FUNCTION_DESCRIPTOR);
323     }
324 
325     private Constant emitConstantSegment(String javaName, Object value) {
326         incrAlign();
327         indent();
328         String fieldName = Constant.Kind.SEGMENT.fieldName(javaName);
329         append(memberMods());
330         append("MemorySegment ");
331         append(fieldName);
332         append(" = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String(\"");
333         append(Utils.quote(Objects.toString(value)));
334         append("\");\n");
335         decrAlign();
336         return new Constant(className(), javaName, Constant.Kind.SEGMENT);
337     }
338 
339     private Constant emitConstantAddress(String javaName, Object value) {
340         incrAlign();
341         indent();
342         String fieldName = Constant.Kind.ADDRESS.fieldName(javaName);
343         append(memberMods());
344         append("MemoryAddress ");
345         append(fieldName);
346         append(" = MemoryAddress.ofLong(");
347         append(((Number)value).longValue());
348         append("L);\n");
349         decrAlign();
350         return new Constant(className(), javaName, Constant.Kind.ADDRESS);
351     }
352 
353     private static String typeToLayoutName(ValueLayout vl) {
354         return Utils.layoutToConstant(vl);
355     }
356 
357     private Constant emitSegmentField(String javaName, String nativeName, MemoryLayout layout) {
358         Constant layoutConstant = addLayout(javaName, layout);
359         incrAlign();
360         indent();
361         String fieldName = Constant.Kind.SEGMENT.fieldName(javaName);
362         append(memberMods());
363         append("MemorySegment ");
364         append(fieldName);
365         append(" = ");
366         append("RuntimeHelper.lookupGlobalVariable(");
367         append("\"" + nativeName + "\", ");
368         append(layoutConstant.accessExpression());
369         append(");\n");
370         decrAlign();
371         return new Constant(className(), javaName, Constant.Kind.SEGMENT);
372     }
373 
374     @Override
375     protected void emitWithConstantClass(Consumer<ConstantBuilder> constantConsumer) {
376         constantConsumer.accept(this);
377     }
378 }