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.incubator.jextract.tool; 26 27 import jdk.incubator.jextract.Declaration; 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.MemoryLayouts; 33 import jdk.incubator.foreign.MemorySegment; 34 import jdk.incubator.foreign.SequenceLayout; 35 import jdk.incubator.foreign.ValueLayout; 36 37 import java.lang.invoke.MethodType; 38 import java.lang.reflect.Field; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.stream.Collectors; 42 import java.util.stream.IntStream; 43 import java.util.stream.Stream; 44 45 /** 46 * A helper class to generate header interface class in source form. 47 * After aggregating various constituents of a .java source, build 48 * method is called to get overall generated source string. 49 */ 50 class JavaSourceBuilder { 51 // buffer 52 protected StringBuffer sb; 53 // current line alignment (number of 4-spaces) 54 protected int align; 55 56 JavaSourceBuilder(int align) { 57 this.align = align; 58 this.sb = new StringBuffer(); 59 } 60 61 JavaSourceBuilder() { 62 this(0); 63 } 64 65 protected int align() { 66 return align; 67 } 68 69 final String PUB_CLS_MODS = "public final "; 70 final String PUB_MODS = "public static final "; 71 final String PRI_MODS = "private static final "; 72 73 protected void addPackagePrefix(String pkgName) { 74 assert pkgName.indexOf('/') == -1 : "package name invalid: " + pkgName; 75 sb.append("// Generated by jextract\n\n"); 76 if (!pkgName.isEmpty()) { 77 sb.append("package "); 78 sb.append(pkgName); 79 sb.append(";\n\n"); 80 } 81 addImportSection(); 82 } 83 84 protected void addImportSection() { 85 sb.append("import java.lang.invoke.MethodHandle;\n"); 86 sb.append("import java.lang.invoke.VarHandle;\n"); 87 sb.append("import jdk.incubator.foreign.*;\n"); 88 sb.append("import jdk.incubator.foreign.MemoryLayout.PathElement;\n"); 89 } 90 91 protected void classBegin(String name) { 92 indent(); 93 sb.append(PUB_CLS_MODS + "class "); 94 sb.append(name); 95 sb.append(" {\n\n"); 96 } 97 98 protected void classEnd() { 99 indent(); 100 sb.append("}\n\n"); 101 } 102 103 protected void addLibraries(String[] libraryNames, String[] libraryPaths) { 104 incrAlign(); 105 indent(); 106 sb.append(PRI_MODS + "LibraryLookup[] LIBRARIES = RuntimeHelper.libraries("); 107 sb.append(stringArray(libraryNames) + ", " + stringArray(libraryPaths) + ");\n"); 108 decrAlign(); 109 } 110 111 private String stringArray(String[] elements) { 112 return Stream.of(elements) 113 .map(n -> "\"" + n + "\"") 114 .collect(Collectors.joining(",", "new String[] {", "}")); 115 } 116 117 protected void addLayout(String elementName, MemoryLayout layout) { 118 incrAlign(); 119 indent(); 120 sb.append(PUB_MODS + "MemoryLayout " + elementName + "$LAYOUT = "); 121 addLayout(layout); 122 sb.append(";\n"); 123 decrAlign(); 124 } 125 126 private void addLayout(MemoryLayout l) { 127 if (l instanceof ValueLayout) { 128 boolean found = false; 129 for (Field fs : MemoryLayouts.SysV.class.getDeclaredFields()) { 130 try { 131 MemoryLayout constant = (MemoryLayout)fs.get(null); 132 if (l.name().isPresent()) { 133 constant = constant.withName(l.name().get()); 134 } 135 if (constant.equals(l)) { 136 found = true; 137 sb.append("MemoryLayouts.SysV." + fs.getName()); 138 break; 139 } 140 } catch (ReflectiveOperationException ex) { 141 throw new AssertionError(ex); 142 } 143 } 144 if (!found) { 145 throw new AssertionError("Should not get here: " + l); 146 } 147 } else if (l instanceof SequenceLayout) { 148 sb.append("MemoryLayout.ofSequence("); 149 if (((SequenceLayout) l).elementCount().isPresent()) { 150 sb.append(((SequenceLayout) l).elementCount().getAsLong() + ", "); 151 } 152 addLayout(((SequenceLayout) l).elementLayout()); 153 sb.append(")"); 154 } else if (l instanceof GroupLayout) { 155 if (((GroupLayout) l).isStruct()) { 156 sb.append("MemoryLayout.ofStruct(\n"); 157 } else { 158 sb.append("MemoryLayout.ofUnion(\n"); 159 } 160 incrAlign(); 161 String delim = ""; 162 for (MemoryLayout e : ((GroupLayout) l).memberLayouts()) { 163 sb.append(delim); 164 indent(); 165 addLayout(e); 166 delim = ",\n"; 167 } 168 sb.append("\n"); 169 decrAlign(); 170 indent(); 171 sb.append(")"); 172 } else { 173 //padding 174 sb.append("MemoryLayout.ofPaddingBits(" + l.bitSize() + ")"); 175 } 176 if (l.name().isPresent()) { 177 sb.append(".withName(\"" + l.name().get() + "\")"); 178 } 179 } 180 181 protected void addVarHandle(String name, Class<?> type, String parentName) { 182 incrAlign(); 183 indent(); 184 String vhName = parentName != null ? 185 parentName + "$" + name : 186 name; 187 sb.append(PUB_MODS + "VarHandle " + vhName + " = "); 188 if (parentName != null) { 189 addHandlePath(type, parentName, name); 190 } else { 191 addHandlePath(type, name); 192 } 193 sb.append(";\n"); 194 decrAlign(); 195 } 196 197 protected void addHandlePath(Class<?> type, String strName, String fieldName) { 198 String ty = type.getName(); 199 if (ty.contains("MemoryAddress")) { 200 ty = "long"; 201 } 202 sb.append(strName + "$LAYOUT.varHandle(" + ty + ".class, "); 203 sb.append("PathElement.groupElement(\"" + fieldName +"\")"); 204 sb.append(")"); 205 } 206 207 protected void addHandlePath(Class<?> type, String varName) { 208 String ty = type.getName(); 209 if (ty.contains("MemoryAddress")) { 210 ty = "long"; 211 } 212 sb.append(varName + "$LAYOUT.varHandle(" + ty + ".class)"); 213 } 214 215 protected void addMethodHandle(Declaration.Function funcTree, MethodType mtype, FunctionDescriptor desc) { 216 incrAlign(); 217 indent(); 218 sb.append(PUB_MODS + "MethodHandle " + funcTree.name() + " = "); 219 sb.append("RuntimeHelper.downcallHandle(\n"); 220 incrAlign(); 221 indent(); 222 sb.append("LIBRARIES, \"" + funcTree.name() + "\""); 223 sb.append(",\n"); 224 indent(); 225 sb.append("\"" + mtype.toMethodDescriptorString() + "\",\n"); 226 indent(); 227 addFunction(desc); 228 decrAlign(); 229 sb.append("\n"); 230 indent(); 231 sb.append(");\n"); 232 decrAlign(); 233 } 234 235 protected void addAddressLookup(String name) { 236 sb.append("RuntimeHelper.lookupGlobalVariable(LIBRARIES, \"" + name + "\")"); 237 } 238 239 private void addFunction(FunctionDescriptor f) { 240 if (f.returnLayout().isPresent()) { 241 sb.append("FunctionDescriptor.of("); 242 addLayout(f.returnLayout().get()); 243 sb.append(", "); 244 } else { 245 sb.append("FunctionDescriptor.ofVoid("); 246 } 247 sb.append(f.isVariadic()); 248 if (!f.argumentLayouts().isEmpty()) { 249 sb.append(",\n"); 250 incrAlign(); 251 String delim = ""; 252 for (MemoryLayout e : f.argumentLayouts()) { 253 sb.append(delim); 254 indent(); 255 addLayout(e); 256 delim = ",\n"; 257 } 258 sb.append("\n"); 259 decrAlign(); 260 indent(); 261 } 262 sb.append(")"); 263 } 264 265 protected void addAddress(String name) { 266 incrAlign(); 267 indent(); 268 sb.append(PUB_MODS + "MemoryAddress " + name + "$ADDR" + " = "); 269 addAddressLookup(name); 270 sb.append(";\n"); 271 decrAlign(); 272 } 273 274 protected void addConstant(String name, Class<?> type, Object value) { 275 incrAlign(); 276 indent(); 277 if (type == MemoryAddress.class || type == MemorySegment.class) { 278 //todo, skip for now (address constants and string constants) 279 } else { 280 sb.append(PUB_MODS + type.getName() + " " + name); 281 sb.append(" = "); 282 if (type == float.class) { 283 sb.append(value); 284 sb.append("f"); 285 } else if (type == long.class) { 286 sb.append(value); 287 sb.append("L"); 288 } else if (type == double.class) { 289 sb.append(value); 290 sb.append("d"); 291 } else { 292 sb.append("(" + type.getName() + ")"); 293 sb.append(value + "L"); 294 } 295 sb.append(";\n"); 296 } 297 298 decrAlign(); 299 } 300 301 static int funcIntfCounter = 0; 302 303 protected void addUpcallFactory(FunctionDescriptor desc) { 304 String fnName = "FI" + funcIntfCounter++; 305 incrAlign(); 306 indent(); 307 sb.append(PRI_MODS + "FunctionDescriptor " + fnName + "$DESC = "); 308 addFunction(desc); 309 sb.append(";\n"); 310 indent(); 311 sb.append(PUB_MODS + "MemoryAddress " + fnName + "$make(MethodHandle handle) {\n"); 312 incrAlign(); 313 indent(); 314 sb.append("return RuntimeHelper.upcallStub(handle, " + fnName + "$DESC);\n"); 315 decrAlign(); 316 indent(); 317 sb.append("}\n"); 318 decrAlign(); 319 } 320 321 protected void addStaticFunctionWrapper(Declaration.Function f, MethodType mtype) { 322 incrAlign(); 323 indent(); 324 sb.append(PUB_MODS + mtype.returnType().getName() + " " + f.name() + " ("); 325 String delim = ""; 326 List<String> pNames = new ArrayList<>(); 327 final int numParams = f.parameters().size(); 328 for (int i = 0 ; i < numParams; i++) { 329 String pName = f.parameters().get(i).name(); 330 if (pName.isEmpty()) { 331 pName = "x" + i; 332 } 333 pNames.add(pName); 334 sb.append(delim + mtype.parameterType(i).getName() + " " + pName); 335 delim = ", "; 336 } 337 if (f.type().varargs()) { 338 String lastArg = "x" + numParams; 339 if (numParams > 0) { 340 sb.append(", "); 341 } 342 sb.append("Object... " + lastArg); 343 pNames.add(lastArg); 344 } 345 sb.append(") {\n"); 346 incrAlign(); 347 indent(); 348 sb.append("try {\n"); 349 incrAlign(); 350 indent(); 351 if (!mtype.returnType().equals(void.class)) { 352 sb.append("return (" + mtype.returnType().getName() + ")"); 353 } 354 sb.append(f.name() + ".invokeExact(" + String.join(", ", pNames) + ");\n"); 355 decrAlign(); 356 indent(); 357 sb.append("} catch (Throwable ex) {\n"); 358 incrAlign(); 359 indent(); 360 sb.append("throw new AssertionError(ex);\n"); 361 decrAlign(); 362 indent(); 363 sb.append("}\n"); 364 decrAlign(); 365 indent(); 366 sb.append("}\n"); 367 decrAlign(); 368 } 369 370 void addDescriptor(String name, FunctionDescriptor desc) { 371 incrAlign(); 372 indent(); 373 sb.append(PRI_MODS + "FunctionDescriptor " + name + "$DESC = "); 374 addFunction(desc); 375 sb.append(";\n"); 376 decrAlign(); 377 indent(); 378 } 379 380 void addFunctionalInterface(String name, MethodType mtype) { 381 incrAlign(); 382 indent(); 383 sb.append("public interface " + name + " {\n"); 384 incrAlign(); 385 indent(); 386 sb.append(mtype.returnType().getName() + " apply("); 387 String delim = ""; 388 for (int i = 0 ; i < mtype.parameterCount() ; i++) { 389 sb.append(delim + mtype.parameterType(i).getName() + " x" + i); 390 delim = ", "; 391 } 392 sb.append(");\n"); 393 decrAlign(); 394 indent(); 395 sb.append("}\n"); 396 decrAlign(); 397 indent(); 398 } 399 400 protected void addFunctionalFactory(String name, MethodType mtype) { 401 incrAlign(); 402 indent(); 403 sb.append(PUB_MODS + "MemoryAddress " + name + "$make(" + name + " fi) {\n"); 404 incrAlign(); 405 indent(); 406 sb.append("return RuntimeHelper.upcallStub(" + name + ".class, fi, " + name + "$DESC, " + 407 "\"" + mtype.toMethodDescriptorString() + "\");\n"); 408 decrAlign(); 409 indent(); 410 sb.append("}\n"); 411 decrAlign(); 412 } 413 414 void addGetter(String name, Class<?> type, Declaration parent) { 415 incrAlign(); 416 indent(); 417 String vhName = (parent != null ? (parent.name() + "$") : "") + name; 418 String param = parent != null ? (MemorySegment.class.getName() + " seg") : ""; 419 sb.append(PUB_MODS + type.getName() + " " + vhName + "$get(" + param + ") {\n"); 420 incrAlign(); 421 indent(); 422 String vhParam = parent != null ? 423 "seg.baseAddress()" : name + "$ADDR"; 424 sb.append("return (" + type.getName() + ")" + vhName + ".get(" + vhParam + ");\n"); 425 decrAlign(); 426 indent(); 427 sb.append("}\n"); 428 decrAlign(); 429 } 430 431 void addSetter(String name, Class<?> type, Declaration parent) { 432 incrAlign(); 433 indent(); 434 String vhName = (parent != null ? (parent.name() + "$") : "") + name; 435 String param = parent != null ? (MemorySegment.class.getName() + " seg, ") : ""; 436 sb.append(PUB_MODS + "void " + vhName + "$set(" + param + type.getName() + " x) {\n"); 437 incrAlign(); 438 indent(); 439 String vhParam = parent != null ? 440 "seg.baseAddress()" : name + "$ADDR"; 441 sb.append(vhName + ".set(" + vhParam + ", x);\n"); 442 decrAlign(); 443 indent(); 444 sb.append("}\n"); 445 decrAlign(); 446 } 447 448 protected String build() { 449 String res = sb.toString(); 450 this.sb = null; 451 return res.toString(); 452 } 453 454 protected void indent() { 455 for (int i = 0; i < align; i++) { 456 sb.append(" "); 457 } 458 } 459 460 protected void incrAlign() { 461 align++; 462 } 463 464 protected void decrAlign() { 465 align--; 466 } 467 }