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         for (int i = 0 ; i < f.parameters().size() ; i++) {
 328             String pName = f.parameters().get(i).name();
 329             if (pName.isEmpty()) {
 330                 pName = "x" + i;
 331             }
 332             pNames.add(pName);
 333             sb.append(delim + mtype.parameterType(i).getName() + " " + pName);
 334             delim = ", ";
 335         }
 336         if (f.type().varargs()) {
 337             String lastArg = "x" + f.parameters().size();
 338             sb.append(", Object... " + lastArg);
 339             pNames.add(lastArg);
 340         }
 341         sb.append(") {\n");
 342         incrAlign();
 343         indent();
 344         sb.append("try {\n");
 345         incrAlign();
 346         indent();
 347         if (!mtype.returnType().equals(void.class)) {
 348             sb.append("return (" + mtype.returnType().getName() + ")");
 349         }
 350         sb.append(f.name() + ".invokeExact(" + String.join(", ", pNames) + ");\n");
 351         decrAlign();
 352         indent();
 353         sb.append("} catch (Throwable ex) {\n");
 354         incrAlign();
 355         indent();
 356         sb.append("throw new AssertionError(ex);\n");
 357         decrAlign();
 358         indent();
 359         sb.append("}\n");
 360         decrAlign();
 361         indent();
 362         sb.append("}\n");
 363         decrAlign();
 364     }
 365 
 366     void addDescriptor(String name, FunctionDescriptor desc) {
 367         incrAlign();
 368         indent();
 369         sb.append(PRI_MODS + "FunctionDescriptor " + name + "$DESC = ");
 370         addFunction(desc);
 371         sb.append(";\n");
 372         decrAlign();
 373         indent();
 374     }
 375 
 376     void addFunctionalInterface(String name, MethodType mtype) {
 377         incrAlign();
 378         indent();
 379         sb.append("public interface " + name + " {\n");
 380         incrAlign();
 381         indent();
 382         sb.append(mtype.returnType().getName() + " apply(");
 383         String delim = "";
 384         for (int i = 0 ; i < mtype.parameterCount() ; i++) {
 385             sb.append(delim + mtype.parameterType(i).getName() + " x" + i);
 386             delim = ", ";
 387         }
 388         sb.append(");\n");
 389         decrAlign();
 390         indent();
 391         sb.append("}\n");
 392         decrAlign();
 393         indent();
 394     }
 395 
 396     protected void addFunctionalFactory(String name, MethodType mtype) {
 397         incrAlign();
 398         indent();
 399         sb.append(PUB_MODS + "MemoryAddress " + name + "$make(" + name + " fi) {\n");
 400         incrAlign();
 401         indent();
 402         sb.append("return RuntimeHelper.upcallStub(" + name + ".class, fi, " + name + "$DESC, " +
 403                 "\"" + mtype.toMethodDescriptorString() + "\");\n");
 404         decrAlign();
 405         indent();
 406         sb.append("}\n");
 407         decrAlign();
 408     }
 409 
 410     void addGetter(String name, Class<?> type, Declaration parent) {
 411         incrAlign();
 412         indent();
 413         String vhName = (parent != null ? (parent.name() + "$") : "") + name;
 414         String param = parent != null ? (MemorySegment.class.getName() + " seg") : "";
 415         sb.append(PUB_MODS + type.getName() + " " + vhName + "$get(" + param + ") {\n");
 416         incrAlign();
 417         indent();
 418         String vhParam = parent != null ?
 419                 "seg.baseAddress()" : name + "$ADDR";
 420         sb.append("return (" + type.getName() + ")" + vhName + ".get(" + vhParam + ");\n");
 421         decrAlign();
 422         indent();
 423         sb.append("}\n");
 424         decrAlign();
 425     }
 426 
 427     void addSetter(String name, Class<?> type, Declaration parent) {
 428         incrAlign();
 429         indent();
 430         String vhName = (parent != null ? (parent.name() + "$") : "") + name;
 431         String param = parent != null ? (MemorySegment.class.getName() + " seg, ") : "";
 432         sb.append(PUB_MODS + "void " + vhName + "$set(" + param + type.getName() + " x) {\n");
 433         incrAlign();
 434         indent();
 435         String vhParam = parent != null ?
 436                 "seg.baseAddress()" : name + "$ADDR";
 437         sb.append(vhName + ".set(" + vhParam + ", x);\n");
 438         decrAlign();
 439         indent();
 440         sb.append("}\n");
 441         decrAlign();
 442     }
 443 
 444     protected String build() {
 445         String res = sb.toString();
 446         this.sb = null;
 447         return res.toString();
 448     }
 449 
 450     protected void indent() {
 451         for (int i = 0; i < align; i++) {
 452             sb.append("    ");
 453         }
 454     }
 455 
 456     protected void incrAlign() {
 457         align++;
 458     }
 459 
 460     protected void decrAlign() {
 461         align--;
 462     }
 463 }