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 }