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