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.MemoryAddress;
 28 import jdk.incubator.foreign.MemorySegment;
 29 import jdk.incubator.foreign.ResourceScope;
 30 import jdk.incubator.foreign.SegmentAllocator;
 31 import jdk.incubator.foreign.ValueLayout;
 32 import jdk.incubator.jextract.Type;
 33 
 34 import jdk.internal.jextract.impl.ConstantBuilder.Constant;
 35 
 36 import java.lang.invoke.MethodType;
 37 import java.util.ArrayList;
 38 import java.util.List;
 39 
 40 /**
 41  * A helper class to generate header interface class in source form.
 42  * After aggregating various constituents of a .java source, build
 43  * method is called to get overall generated source string.
 44  */
 45 abstract class HeaderFileBuilder extends ClassSourceBuilder {
 46 
 47     static final String MEMBER_MODS = "public static";
 48 
 49     private final String superclass;
 50 
 51     HeaderFileBuilder(ToplevelBuilder enclosing, String name, String superclass) {
 52         super(enclosing, Kind.CLASS, name);
 53         this.superclass = superclass;
 54     }
 55 
 56     @Override
 57     String superClass() {
 58         return superclass;
 59     }
 60 
 61     @Override
 62     public void addVar(String javaName, String nativeName, VarInfo varInfo) {
 63         if (varInfo.carrier().equals(MemorySegment.class)) {
 64             emitWithConstantClass(constantBuilder -> {
 65                 constantBuilder.addSegment(javaName, nativeName, varInfo.layout())
 66                         .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME, nativeName);
 67             });
 68         } else {
 69             emitWithConstantClass(constantBuilder -> {
 70                 constantBuilder.addLayout(javaName, varInfo.layout())
 71                         .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME);
 72                 Constant vhConstant = constantBuilder.addGlobalVarHandle(javaName, nativeName, varInfo)
 73                         .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME);
 74                 Constant segmentConstant = constantBuilder.addSegment(javaName, nativeName, varInfo.layout())
 75                         .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME, nativeName);
 76                 emitGlobalGetter(segmentConstant, vhConstant, javaName, nativeName, varInfo.carrier());
 77                 emitGlobalSetter(segmentConstant, vhConstant, javaName, nativeName, varInfo.carrier());
 78                 if (varInfo.fiName().isPresent()) {
 79                     emitFunctionalInterfaceGetter(varInfo.fiName().get(), javaName);
 80                 }
 81             });
 82         }
 83     }
 84 
 85     @Override
 86     public void addFunction(String javaName, String nativeName, FunctionInfo functionInfo) {
 87         emitWithConstantClass(constantBuilder -> {
 88             Constant mhConstant = constantBuilder.addMethodHandle(javaName, nativeName, functionInfo, false)
 89                     .emitGetter(this, MEMBER_MODS, Constant.QUALIFIED_NAME, nativeName);
 90             emitFunctionWrapper(mhConstant, javaName, nativeName, functionInfo);
 91             if (functionInfo.methodType().returnType().equals(MemorySegment.class)) {
 92                 // emit scoped overload
 93                 emitFunctionWrapperScopedOverload(javaName, functionInfo);
 94             }
 95         });
 96     }
 97 
 98     @Override
 99     public void addConstant(String javaName, Class<?> type, Object value) {
100         if (type.equals(MemorySegment.class) || type.equals(MemoryAddress.class)) {
101             emitWithConstantClass(constantBuilder -> {
102                 constantBuilder.addConstantDesc(javaName, type, value)
103                         .emitGetter(this, MEMBER_MODS, Constant.JAVA_NAME);
104             });
105         } else {
106             emitGetter(MEMBER_MODS, type, javaName, getConstantString(type, value));
107         }
108     }
109 
110     // private generation
111 
112     private void emitFunctionWrapper(Constant mhConstant, String javaName, String nativeName, FunctionInfo functionInfo) {
113         incrAlign();
114         indent();
115         append(MEMBER_MODS + " ");
116         MethodType declType = functionInfo.methodType();
117         List<String> paramNames = functionInfo.parameterNames().get();
118         if (functionInfo.methodType().returnType().equals(MemorySegment.class)) {
119             // needs allocator parameter
120             declType = declType.insertParameterTypes(0, SegmentAllocator.class);
121             paramNames = new ArrayList<>(paramNames);
122             paramNames.add(0, "allocator");
123         }
124         List<String> pExprs = emitFunctionWrapperDecl(javaName, declType, functionInfo.isVarargs(), paramNames);
125         append(" {\n");
126         incrAlign();
127         indent();
128         append("var mh$ = RuntimeHelper.requireNonNull(");
129         append(mhConstant.accessExpression());
130         append(", \"");
131         append(nativeName);
132         append("\");\n");
133         indent();
134         append("try {\n");
135         incrAlign();
136         indent();
137         if (!functionInfo.methodType().returnType().equals(void.class)) {
138             append("return (" + functionInfo.methodType().returnType().getName() + ")");
139         }
140         append("mh$.invokeExact(" + String.join(", ", pExprs) + ");\n");
141         decrAlign();
142         indent();
143         append("} catch (Throwable ex$) {\n");
144         incrAlign();
145         indent();
146         append("throw new AssertionError(\"should not reach here\", ex$);\n");
147         decrAlign();
148         indent();
149         append("}\n");
150         decrAlign();
151         indent();
152         append("}\n");
153         decrAlign();
154     }
155 
156     private void emitFunctionWrapperScopedOverload(String javaName, FunctionInfo functionInfo) {
157         incrAlign();
158         indent();
159         append(MEMBER_MODS + " ");
160         List<String> paramNames = new ArrayList<>(functionInfo.parameterNames().get());
161         paramNames.add(0, "scope");
162         List<String> pExprs = emitFunctionWrapperDecl(javaName,
163         functionInfo.methodType().insertParameterTypes(0, ResourceScope.class),
164         functionInfo.isVarargs(),
165         paramNames);
166         String param = pExprs.remove(0);
167         pExprs.add(0, "SegmentAllocator.nativeAllocator(" + param + ")");
168         append(" {\n");
169         incrAlign();
170         indent();
171         if (!functionInfo.methodType().returnType().equals(void.class)) {
172             append("return ");
173         }
174         append(javaName + "(" + String.join(", ", pExprs) + ");\n");
175         decrAlign();
176         indent();
177         append("}\n");
178         decrAlign();
179     }
180 
181     private List<String> emitFunctionWrapperDecl(String javaName, MethodType methodType, boolean isVarargs, List<String> paramNames) {
182         append(methodType.returnType().getSimpleName() + " " + javaName + " (");
183         String delim = "";
184         List<String> pExprs = new ArrayList<>();
185         final int numParams = paramNames.size();
186         for (int i = 0 ; i < numParams; i++) {
187             String pName = paramNames.get(i);
188             if (pName.isEmpty()) {
189                 pName = "x" + i;
190             }
191             pExprs.add(pName);
192             Class<?> pType = methodType.parameterType(i);
193             append(delim + " " + pType.getSimpleName() + " " + pName);
194             delim = ", ";
195         }
196         if (isVarargs) {
197             String lastArg = "x" + numParams;
198             append(delim + "Object... " + lastArg);
199             pExprs.add(lastArg);
200         }
201         append(")");
202         return pExprs;
203     }
204 
205     private void emitFunctionalInterfaceGetter(String fiName, String javaName) {
206         incrAlign();
207         indent();
208         append(MEMBER_MODS + " ");
209         append(fiName + " " + javaName + " () {\n");
210         incrAlign();
211         indent();
212         append("return " + fiName + ".ofAddress(" + javaName + "$get(), ResourceScope.globalScope());\n");
213         decrAlign();
214         indent();
215         append("}\n");
216         decrAlign();
217     }
218 
219     void emitPrimitiveTypedef(Type.Primitive primType, String name) {
220         Type.Primitive.Kind kind = primType.kind();
221         if (primitiveKindSupported(kind) && !kind.layout().isEmpty()) {
222             incrAlign();
223             indent();
224             append(MEMBER_MODS);
225             append(" " + primType.kind().layout().orElseThrow().getClass().getSimpleName());
226             append(" " + uniqueNestedClassName(name));
227             append(" = ");
228             append(toplevel().rootConstants().resolvePrimitiveLayout((ValueLayout)kind.layout().get()).accessExpression());
229             append(";\n");
230             decrAlign();
231         }
232     }
233 
234     void emitPointerTypedef(String name) {
235         incrAlign();
236         indent();
237         append(MEMBER_MODS);
238         append(" OfAddress ");
239         append(uniqueNestedClassName(name));
240         append(" = ");
241         append(toplevel().rootConstants().resolvePrimitiveLayout(TypeImpl.PointerImpl.POINTER_LAYOUT).accessExpression());
242         append(";\n");
243         decrAlign();
244     }
245 
246     private boolean primitiveKindSupported(Type.Primitive.Kind kind) {
247         return switch(kind) {
248             case Short, Int, Long, LongLong, Float, Double, Char -> true;
249             default -> false;
250         };
251     }
252 
253     private String getConstantString(Class<?> type, Object value) {
254         StringBuilder buf = new StringBuilder();
255         if (type == boolean.class) {
256             buf.append(value);
257         } else if (type == float.class) {
258             float f = ((Number)value).floatValue();
259             if (Float.isFinite(f)) {
260                 buf.append(value);
261                 buf.append("f");
262             } else {
263                 buf.append("Float.valueOf(\"");
264                 buf.append(value.toString());
265                 buf.append("\")");
266             }
267         } else if (type == long.class) {
268             buf.append(value);
269             buf.append("L");
270         } else if (type == double.class) {
271             double d = ((Number)value).doubleValue();
272             if (Double.isFinite(d)) {
273                 buf.append(value);
274                 buf.append("d");
275             } else {
276                 buf.append("Double.valueOf(\"");
277                 buf.append(value.toString());
278                 buf.append("\")");
279             }
280         } else {
281             buf.append("(" + type.getName() + ")");
282             buf.append(value + "L");
283         }
284         return buf.toString();
285     }
286 
287     private void emitGlobalGetter(Constant segmentConstant, Constant vhConstant, String javaName, String nativeName, Class<?> type) {
288         incrAlign();
289         indent();
290         append(MEMBER_MODS + " " + type.getSimpleName() + " " + javaName + "$get() {\n");
291         incrAlign();
292         indent();
293         append("return (" + type.getName() + ") ");
294         append(vhConstant.accessExpression());
295         append(".get(RuntimeHelper.requireNonNull(");
296         append(segmentConstant.accessExpression());
297         append(", \"");
298         append(nativeName);
299         append("\"));\n");
300         decrAlign();
301         indent();
302         append("}\n");
303         decrAlign();
304     }
305 
306     private void emitGlobalSetter(Constant segmentConstant, Constant vhConstant, String javaName, String nativeName, Class<?> type) {
307         incrAlign();
308         indent();
309         append(MEMBER_MODS + " void " + javaName + "$set(" + " " + type.getSimpleName() + " x) {\n");
310         incrAlign();
311         indent();
312         append(vhConstant.accessExpression());
313         append(".set(RuntimeHelper.requireNonNull(");
314         append(segmentConstant.accessExpression());
315         append(", \"");
316         append(nativeName);
317         append("\"), x);\n");
318         decrAlign();
319         indent();
320         append("}\n");
321         decrAlign();
322     }
323 }