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