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 }