< prev index next >

src/jdk.jextract/share/classes/com/sun/tools/jextract/AsmCodeFactory.java

Print this page




   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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package com.sun.tools.jextract;
  24 
  25 import java.io.IOException;
  26 import java.foreign.layout.Layout;

  27 import java.nio.file.Path;
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.HashMap;
  31 import java.util.HashSet;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Set;
  35 import java.util.logging.Logger;
  36 
  37 import jdk.internal.clang.SourceLocation;
  38 import jdk.internal.clang.Type;
  39 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  40 import jdk.internal.org.objectweb.asm.ClassVisitor;
  41 import jdk.internal.org.objectweb.asm.ClassWriter;
  42 import jdk.internal.org.objectweb.asm.MethodVisitor;
  43 import com.sun.tools.jextract.tree.EnumTree;
  44 import com.sun.tools.jextract.tree.FieldTree;
  45 import com.sun.tools.jextract.tree.FunctionTree;
  46 import com.sun.tools.jextract.tree.MacroTree;


  59 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
  60 import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
  61 import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN;
  62 import static jdk.internal.org.objectweb.asm.Opcodes.I2C;
  63 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
  64 import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
  65 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
  66 
  67 /**
  68  * Scan a header file and generate classes for entities defined in that header
  69  * file. Tree visitor visit methods return true/false depending on whether a
  70  * particular Tree is processed or skipped.
  71  */
  72 class AsmCodeFactory extends SimpleTreeVisitor<Boolean, JType> {
  73     private static final String ANNOTATION_PKG_PREFIX = "Ljava/foreign/annotations/";
  74     private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;";
  75     private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";
  76     private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;";
  77     private static final String NATIVE_STRUCT = ANNOTATION_PKG_PREFIX + "NativeStruct;";
  78 
  79     private final Context ctx;
  80     private final ClassWriter global_cw;
  81     // to avoid duplicate generation of methods, field accessors, macros
  82     private final Set<String> global_methods = new HashSet<>();
  83     private final Set<String> global_fields = new HashSet<>();
  84     private final Set<String> global_macros = new HashSet<>();
  85     private final List<String> headerDeclarations = new ArrayList<>();
  86     private transient boolean built = false;
  87     protected final String headerClassName;
  88     protected final HeaderFile headerFile;
  89     protected final TypeDictionary dict;
  90     protected final Map<String, byte[]> types;




  91     protected final Logger logger = Logger.getLogger(getClass().getPackage().getName());
  92 
  93     AsmCodeFactory(Context ctx, HeaderFile header) {
  94         this.ctx = ctx;
  95         logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
  96         this.headerFile = header;
  97         this.headerClassName = Utils.toInternalName(headerFile.pkgName, headerFile.clsName);
  98         this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
  99         this.types = new HashMap<>();
 100         this.dict = headerFile.dictionary();



 101         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 102                 headerClassName,
 103                 null, "java/lang/Object", null);
 104     }
 105 
 106     private void generateNativeHeader() {







 107         AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
 108         av.visit("path", headerFile.path.toAbsolutePath().toString());
 109         if (headerFile.libraries != null && !headerFile.libraries.isEmpty()) {
 110             AnnotationVisitor libNames = av.visitArray("libraries");
 111             for (String name : headerFile.libraries) {
 112                 libNames.visit(null, name);
 113             }
 114             libNames.visitEnd();
 115             if (headerFile.libraryPaths != null && !headerFile.libraryPaths.isEmpty()) {
 116                 AnnotationVisitor libPaths = av.visitArray("libraryPaths");
 117                 for (String path : headerFile.libraryPaths) {
 118                     libPaths.visit(null, path);
 119                 }
 120                 libPaths.visitEnd();
 121             }
 122         }
 123         av.visit("declarations", String.join(" ", headerDeclarations));
 124         av.visitEnd();
 125         //generate functional interfaces
 126         dict.functionalInterfaces().forEach(fi -> createFunctionalInterface((JType.FunctionalInterfaceType)fi));

 127     }
 128 
 129     private void handleException(Exception ex) {
 130         ctx.err.println(Main.format("cannot.write.class.file", headerFile.pkgName + "." + headerFile.clsName, ex));
 131         if (Main.DEBUG) {
 132             ex.printStackTrace(ctx.err);
 133         }
 134     }
 135 
 136     private void annotateNativeLocation(ClassVisitor cw, Tree tree) {
 137         if (! ctx.getNoNativeLocations()) {
 138             AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true);
 139             SourceLocation src = tree.location();
 140             SourceLocation.Location loc = src.getFileLocation();
 141             Path p = loc.path();
 142             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 143             av.visit("line", loc.line());
 144             av.visit("column", loc.column());
 145             av.visitEnd();
 146         }
 147     }
 148 
 149     private void writeClassFile(final ClassWriter cw, String clsName)
 150             throws IOException {
 151         cw.visitEnd();
 152         byte[] bytecodes = cw.toByteArray();
 153         if (null != types.put(clsName, bytecodes)) {
 154             logger.warning("Class " + clsName + " definition is overwritten");
 155         }
 156     }
 157 
 158     private static boolean isBitField(Tree tree) {
 159         return tree instanceof FieldTree && ((FieldTree)tree).isBitField();
 160     }
 161 
 162     /**
 163      *
 164      * @param cw ClassWriter for the struct
 165      * @param tree The Tree
 166      * @param parentType The struct type
 167      */
 168     private boolean addField(ClassVisitor cw, Tree tree, Type parentType) {
 169         String fieldName = tree.name();
 170         assert !fieldName.isEmpty();
 171         Type type = tree.type();
 172         JType jt = dict.lookup(type);
 173         assert (jt != null);
 174         if (cw == global_cw) {
 175             String uniqueName = fieldName + "." + jt.getDescriptor();
 176             if (! global_fields.add(uniqueName)) {
 177                 return false; // added already
 178             }
 179         }
 180         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
 181                 "()" + jt.getDescriptor(), "()" + jt.getSignature(false), null);
 182 
 183         if (! ctx.getNoNativeLocations()) {
 184             AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 185             SourceLocation src = tree.location();
 186             SourceLocation.Location loc = src.getFileLocation();
 187             Path p = loc.path();
 188             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 189             av.visit("line", loc.line());
 190             av.visit("column", loc.column());
 191             av.visitEnd();
 192         }
 193 
 194         mv.visitEnd();
 195         mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set",
 196                 "(" + jt.getDescriptor() + ")V",
 197                 "(" + jt.getSignature(true) + ")V", null);
 198         mv.visitEnd();
 199         if (tree instanceof VarTree || !isBitField(tree)) {
 200             JType ptrType = JType.GenericType.ofPointer(jt);
 201             mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr",
 202                     "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(false), null);
 203             mv.visitEnd();


 208 
 209     @Override
 210     public Boolean visitVar(VarTree varTree, JType jt) {
 211         if (addField(global_cw, varTree, null)) {
 212             Layout layout = varTree.layout();
 213             String descStr = decorateAsAccessor(varTree, layout).toString();
 214             addHeaderDecl(varTree.name(), descStr);
 215             return true;
 216         } else {
 217             return false;
 218         }
 219     }
 220 
 221     private void addHeaderDecl(String symbol, String desc) {
 222         headerDeclarations.add(String.format("%s=%s", symbol, desc));
 223     }
 224 
 225     private void addConstant(ClassWriter cw, FieldTree fieldTree) {
 226         assert (fieldTree.isEnumConstant());
 227         String name = fieldTree.name();
 228         String desc = dict.lookup(fieldTree.type()).getDescriptor();
 229         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, "()" + desc, null, null);
 230         mv.visitCode();
 231         if (desc.length() != 1) {
 232             throw new AssertionError("expected single char descriptor: " + desc);
 233         }
 234         switch (desc.charAt(0)) {
 235             case 'J':
 236                 long lvalue = fieldTree.enumConstant().get();
 237                 mv.visitLdcInsn(lvalue);
 238                 mv.visitInsn(LRETURN);
 239                 break;
 240             case 'I':
 241                 int ivalue = fieldTree.enumConstant().get().intValue();
 242                 mv.visitLdcInsn(ivalue);
 243                 mv.visitInsn(IRETURN);
 244                 break;
 245             default:
 246                 throw new AssertionError("should not reach here");
 247         }
 248         mv.visitMaxs(1, 1);
 249         mv.visitEnd();
 250     }
 251 
 252     @Override
 253     public Boolean visitStruct(StructTree structTree, JType jt) {







 254         String nativeName = structTree.name();
 255         Type type = structTree.type();
 256         logger.fine(() -> "Create struct: " + nativeName);
 257 
 258         String intf = Utils.toClassName(nativeName);
 259         String name = headerClassName + "$" + intf;
 260 
 261         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 262         /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
 263          * taking static as a valid flag, so comment this out during development.
 264          */
 265         global_cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 266         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 267         cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
 268                 name, "Ljava/lang/Object;Ljava/foreign/memory/Struct<L" + name + ";>;",
 269                 "java/lang/Object", new String[] {"java/foreign/memory/Struct"});
 270         annotateNativeLocation(cw, structTree);
 271 
 272         AnnotationVisitor av = cw.visitAnnotation(NATIVE_STRUCT, true);
 273         Layout structLayout = structTree.layout(this::decorateAsAccessor);
 274         av.visit("value", structLayout.toString());
 275         av.visitEnd();
 276         cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 277 
 278         // fields
 279         structTree.fields().forEach(fieldTree -> addField(cw, fieldTree, type));
 280         // Write class
 281         try {
 282             writeClassFile(cw, headerFile.clsName + "$" + intf);
 283         } catch (IOException ex) {
 284             handleException(ex);
 285         }
 286         return true;
 287     }
 288 
 289     Layout addGetterSetterName(Layout layout, String accessorName) {
 290         return layout
 291             .withAnnotation("get", accessorName + "$get")
 292             .withAnnotation("set", accessorName + "$set");
 293     }
 294 
 295     Layout decorateAsAccessor(VarTree varTree, Layout layout) {
 296         return addGetterSetterName(layout, varTree.name()).
 297             withAnnotation("ptr", varTree.name() + "$ptr");
 298     }
 299 
 300     Layout decorateAsAccessor(FieldTree fieldTree, Layout layout) {
 301         layout = addGetterSetterName(layout, fieldTree.name());
 302         if (!fieldTree.isBitField()) {
 303             //no pointer accessors for bitfield!
 304             layout = layout.withAnnotation("ptr", fieldTree.name() + "$ptr");
 305         }


 328         String intf = Utils.toClassName(nativeName);
 329         String name = headerClassName + "$" + intf;
 330 
 331         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 332         global_cw.visitInnerClass(name, headerClassName, intf,
 333                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 334         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 335         String[] superAnno = { "java/lang/annotation/Annotation" };
 336         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 337                 name, null, "java/lang/Object", superAnno);
 338         annotateNativeLocation(cw, tree);
 339         AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 340         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 341         av.visitEnd();
 342         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 343         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 344         av.visitEnd();
 345         cw.visitInnerClass(name, headerClassName, intf,
 346                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 347         // Write class
 348         try {
 349             writeClassFile(cw, headerFile.clsName + "$" + intf);
 350         } catch (IOException ex) {
 351             handleException(ex);
 352         }
 353     }
 354 
 355     private void createFunctionalInterface(JType.FunctionalInterfaceType fnif) {
 356         JType.Function fn = fnif.getFunction();
 357         String intf;
 358         String nativeName;
 359         String nDesc = fnif.getFunction().getNativeDescriptor();
 360         intf = fnif.getSimpleName();
 361         nativeName = "anonymous function";
 362         logger.fine(() -> "Create FunctionalInterface " + intf);
 363 
 364         final String name = headerClassName + "$" + intf;
 365 
 366         logger.fine(() -> "Define class " + name + " for native type " + nativeName + nDesc);
 367         global_cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 368         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 369         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 370                 name, "Ljava/lang/Object;",
 371                 "java/lang/Object", new String[0]);
 372         AnnotationVisitor av = cw.visitAnnotation(
 373                 "Ljava/lang/FunctionalInterface;", true);
 374         av.visitEnd();
 375         av = cw.visitAnnotation(NATIVE_CALLBACK, true);
 376         av.visit("value", nDesc);
 377         av.visitEnd();
 378         cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 379 
 380         // add the method
 381 
 382         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 383         if (fn.isVarArgs) {
 384             flags |= ACC_VARARGS;
 385         }
 386         MethodVisitor mv = cw.visitMethod(flags, "fn",
 387                 fn.getDescriptor(), fn.getSignature(false), null);
 388         mv.visitEnd();
 389         // Write class
 390         try {
 391             writeClassFile(cw, headerFile.clsName + "$" + intf);
 392         } catch (IOException ex) {
 393             handleException(ex);
 394         }
 395     }
 396 
 397     @Override
 398     public Boolean visitTypedef(TypedefTree typedefTree, JType jt) {
 399         createAnnotationCls(typedefTree);
 400         return true;
 401     }
 402 
 403     @Override
 404     public Boolean visitTree(Tree tree, JType jt) {
 405         logger.warning(() -> "Unsupported declaration tree:");
 406         logger.warning(() -> tree.toString());
 407         return true;
 408     }
 409 
 410     @Override
 411     public Boolean visitFunction(FunctionTree funcTree, JType jt) {
 412         assert (jt instanceof JType.Function);
 413         JType.Function fn = (JType.Function)jt;
 414         String uniqueName = funcTree.name() + "." + fn.getDescriptor();
 415         if (! global_methods.add(uniqueName)) {
 416             return false; // added already
 417         }
 418         logger.fine(() -> "Add method: " + fn.getSignature(false));
 419         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 420         if (fn.isVarArgs) {
 421             flags |= ACC_VARARGS;
 422         }
 423         MethodVisitor mv = global_cw.visitMethod(flags,
 424                 funcTree.name(), fn.getDescriptor(), fn.getSignature(false), null);
 425         final int arg_cnt = funcTree.numParams();
 426         for (int i = 0; i < arg_cnt; i++) {
 427             String name = funcTree.paramName(i);
 428             final int tmp = i;
 429             logger.finer(() -> "  arg " + tmp + ": " + name);
 430             mv.visitParameter(name, 0);
 431         }
 432 
 433         if (! ctx.getNoNativeLocations()) {
 434             AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 435             SourceLocation src = funcTree.location();
 436             SourceLocation.Location loc = src.getFileLocation();
 437             Path p = loc.path();
 438             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 439             av.visit("line", loc.line());
 440             av.visit("column", loc.column());
 441             av.visitEnd();
 442         }
 443 
 444         Type type = funcTree.type();
 445         final String descStr = Utils.getFunction(type).toString();
 446         addHeaderDecl(funcTree.name(), descStr);
 447 
 448         mv.visitEnd();
 449         return true;
 450     }
 451 
 452     protected AsmCodeFactory addType(JType jt, Tree tree) {
 453         try {
 454             logger.fine(() -> "Process tree " + tree.name());
 455             tree.accept(this, jt);
 456         } catch (Exception ex) {
 457             handleException(ex);
 458             logger.warning("Tree causing above exception is: " + tree.name());
 459             logger.warning(() -> tree.toString());
 460         }
 461         return this;
 462     }
 463 
 464     @Override
 465     public Boolean visitMacro(MacroTree macroTree, JType jt) {
 466         if (!macroTree.isConstant()) {
 467             logger.fine(() -> "Skipping unrecognized object-like macro " + macroTree.name());
 468             return false;
 469         }
 470         String name = macroTree.name();
 471         Object value = macroTree.value().get();
 472         if (! global_macros.add(name)) {
 473             return false; // added already
 474         }
 475         logger.fine(() -> "Adding macro " + name);
 476         Class<?> macroType = Utils.unboxIfNeeded(value.getClass());
 477 
 478         String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(macroType));
 479         MethodVisitor mv = global_cw.visitMethod(ACC_PUBLIC, name, sig, sig, null);
 480 
 481         if (! ctx.getNoNativeLocations()) {
 482             AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 483             SourceLocation src = macroTree.location();
 484             SourceLocation.Location loc = src.getFileLocation();
 485             Path p = loc.path();
 486             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 487             av.visit("line", loc.line());
 488             av.visit("column", loc.column());
 489             av.visitEnd();
 490         }
 491 
 492         mv.visitCode();
 493 
 494         mv.visitLdcInsn(value);
 495         if (macroType.equals(char.class)) {
 496             mv.visitInsn(I2C);
 497             mv.visitInsn(IRETURN);
 498         } else if (macroType.equals(int.class)) {
 499             mv.visitInsn(IRETURN);
 500         } else if (macroType.equals(float.class)) {
 501             mv.visitInsn(FRETURN);
 502         } else if (macroType.equals(long.class)) {
 503             mv.visitInsn(LRETURN);
 504         } else if (macroType.equals(double.class)) {
 505             mv.visitInsn(DRETURN);
 506         } else if (macroType.equals(String.class)) {
 507             mv.visitInsn(ARETURN);
 508         }
 509         mv.visitMaxs(0, 0);
 510         mv.visitEnd();
 511         return true;
 512     }
 513 
 514     protected synchronized void produce() {
 515         if (built) {
 516             throw new IllegalStateException("Produce is called multiple times");
 517         }
 518         built = true;
 519         generateNativeHeader();
 520         try {
 521             writeClassFile(global_cw, headerFile.clsName);
 522         } catch (IOException ex) {
 523             handleException(ex);
 524         }
 525     }
 526 
 527     protected Map<String, byte[]> collect() {
 528         // Ensure classes are produced
 529         if (!built) produce();
 530         HashMap<String, byte[]> rv = new HashMap<>();
 531         // Not copying byte[] for efficiency, perhaps not a safe idea though
 532         if (headerFile.pkgName.isEmpty()) {
 533             types.forEach((clsName, bytecodes) -> {
 534                 rv.put(clsName, bytecodes);
 535             });
 536         } else {
 537             types.forEach((clsName, bytecodes) -> {
 538                 rv.put(headerFile.pkgName + "." + clsName, bytecodes);
 539             });
 540         }
 541         return Collections.unmodifiableMap(rv);
 542     }
 543 }


   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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package com.sun.tools.jextract;
  24 

  25 import java.foreign.layout.Layout;
  26 import java.io.PrintWriter;
  27 import java.nio.file.Path;
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.HashMap;
  31 import java.util.HashSet;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Set;
  35 import java.util.logging.Logger;
  36 
  37 import jdk.internal.clang.SourceLocation;
  38 import jdk.internal.clang.Type;
  39 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  40 import jdk.internal.org.objectweb.asm.ClassVisitor;
  41 import jdk.internal.org.objectweb.asm.ClassWriter;
  42 import jdk.internal.org.objectweb.asm.MethodVisitor;
  43 import com.sun.tools.jextract.tree.EnumTree;
  44 import com.sun.tools.jextract.tree.FieldTree;
  45 import com.sun.tools.jextract.tree.FunctionTree;
  46 import com.sun.tools.jextract.tree.MacroTree;


  59 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
  60 import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
  61 import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN;
  62 import static jdk.internal.org.objectweb.asm.Opcodes.I2C;
  63 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
  64 import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
  65 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
  66 
  67 /**
  68  * Scan a header file and generate classes for entities defined in that header
  69  * file. Tree visitor visit methods return true/false depending on whether a
  70  * particular Tree is processed or skipped.
  71  */
  72 class AsmCodeFactory extends SimpleTreeVisitor<Boolean, JType> {
  73     private static final String ANNOTATION_PKG_PREFIX = "Ljava/foreign/annotations/";
  74     private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;";
  75     private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";
  76     private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;";
  77     private static final String NATIVE_STRUCT = ANNOTATION_PKG_PREFIX + "NativeStruct;";
  78 

  79     private final ClassWriter global_cw;
  80     // to avoid duplicate generation of methods, field accessors, macros
  81     private final Set<String> global_methods = new HashSet<>();
  82     private final Set<String> global_fields = new HashSet<>();
  83     private final Set<String> global_macros = new HashSet<>();
  84     private final List<String> headerDeclarations = new ArrayList<>();

  85     protected final String headerClassName;
  86     protected final HeaderFile headerFile;

  87     protected final Map<String, byte[]> types;
  88     protected final List<String> libraryNames;
  89     protected final List<String> libraryPaths;
  90     protected final PrintWriter err;
  91     protected final boolean noNativeLocations;
  92     protected final Logger logger = Logger.getLogger(getClass().getPackage().getName());
  93 
  94     AsmCodeFactory(Context ctx, HeaderFile header) {

  95         logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
  96         this.headerFile = header;
  97         this.headerClassName = Utils.toInternalName(headerFile.pkgName, headerFile.clsName);
  98         this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
  99         this.types = new HashMap<>();
 100         this.libraryNames = ctx.libraryNames;
 101         this.libraryPaths = ctx.libraryPaths;
 102         this.err = ctx.err;
 103         this.noNativeLocations = ctx.noNativeLocations;
 104         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 105                 headerClassName,
 106                 null, "java/lang/Object", null);
 107     }
 108 
 109     public Map<String, byte[]> generateNativeHeader(List<Tree> decls) {
 110         //generate all decls
 111         decls.forEach(this::generateDecl);
 112         //generate functional interfaces
 113         headerFile.dictionary().functionalInterfaces()
 114                 .forEach(fi -> createFunctionalInterface((JType.FunctionalInterfaceType)fi));
 115 
 116         //generate header intf
 117         AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
 118         av.visit("path", headerFile.path.toAbsolutePath().toString());
 119         if (!libraryNames.isEmpty()) {
 120             AnnotationVisitor libNames = av.visitArray("libraries");
 121             for (String name : libraryNames) {
 122                 libNames.visit(null, name);
 123             }
 124             libNames.visitEnd();
 125             if (!libraryPaths.isEmpty()) {
 126                 AnnotationVisitor libPaths = av.visitArray("libraryPaths");
 127                 for (String path : libraryPaths) {
 128                     libPaths.visit(null, path);
 129                 }
 130                 libPaths.visitEnd();
 131             }
 132         }
 133         av.visit("declarations", String.join(" ", headerDeclarations));
 134         av.visitEnd();
 135         global_cw.visitEnd();
 136         addClassIfNeeded(headerClassName, global_cw.toByteArray());
 137         return Collections.unmodifiableMap(types);
 138     }
 139 
 140     private void handleException(Exception ex) {
 141         err.println(Main.format("cannot.write.class.file", headerFile.pkgName + "." + headerFile.clsName, ex));
 142         if (Main.DEBUG) {
 143             ex.printStackTrace(err);
 144         }
 145     }
 146 
 147     private void annotateNativeLocation(ClassVisitor cw, Tree tree) {
 148         if (! noNativeLocations) {
 149             AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true);
 150             SourceLocation src = tree.location();
 151             SourceLocation.Location loc = src.getFileLocation();
 152             Path p = loc.path();
 153             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 154             av.visit("line", loc.line());
 155             av.visit("column", loc.column());
 156             av.visitEnd();
 157         }
 158     }
 159 
 160     private void addClassIfNeeded(String clsName, byte[] bytes) {
 161         if (null != types.put(clsName, bytes)) {
 162             err.println(Main.format("warn.class.overwritten", clsName));



 163         }
 164     }
 165 
 166     private static boolean isBitField(Tree tree) {
 167         return tree instanceof FieldTree && ((FieldTree)tree).isBitField();
 168     }
 169 
 170     /**
 171      *
 172      * @param cw ClassWriter for the struct
 173      * @param tree The Tree
 174      * @param parentType The struct type
 175      */
 176     private boolean addField(ClassVisitor cw, Tree tree, Type parentType) {
 177         String fieldName = tree.name();
 178         assert !fieldName.isEmpty();
 179         Type type = tree.type();
 180         JType jt = headerFile.dictionary().lookup(type);
 181         assert (jt != null);
 182         if (cw == global_cw) {
 183             String uniqueName = fieldName + "." + jt.getDescriptor();
 184             if (! global_fields.add(uniqueName)) {
 185                 return false; // added already
 186             }
 187         }
 188         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
 189                 "()" + jt.getDescriptor(), "()" + jt.getSignature(false), null);
 190 
 191         if (! noNativeLocations) {
 192             AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 193             SourceLocation src = tree.location();
 194             SourceLocation.Location loc = src.getFileLocation();
 195             Path p = loc.path();
 196             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 197             av.visit("line", loc.line());
 198             av.visit("column", loc.column());
 199             av.visitEnd();
 200         }
 201 
 202         mv.visitEnd();
 203         mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set",
 204                 "(" + jt.getDescriptor() + ")V",
 205                 "(" + jt.getSignature(true) + ")V", null);
 206         mv.visitEnd();
 207         if (tree instanceof VarTree || !isBitField(tree)) {
 208             JType ptrType = JType.GenericType.ofPointer(jt);
 209             mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr",
 210                     "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(false), null);
 211             mv.visitEnd();


 216 
 217     @Override
 218     public Boolean visitVar(VarTree varTree, JType jt) {
 219         if (addField(global_cw, varTree, null)) {
 220             Layout layout = varTree.layout();
 221             String descStr = decorateAsAccessor(varTree, layout).toString();
 222             addHeaderDecl(varTree.name(), descStr);
 223             return true;
 224         } else {
 225             return false;
 226         }
 227     }
 228 
 229     private void addHeaderDecl(String symbol, String desc) {
 230         headerDeclarations.add(String.format("%s=%s", symbol, desc));
 231     }
 232 
 233     private void addConstant(ClassWriter cw, FieldTree fieldTree) {
 234         assert (fieldTree.isEnumConstant());
 235         String name = fieldTree.name();
 236         String desc = headerFile.dictionary().lookup(fieldTree.type()).getDescriptor();
 237         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, "()" + desc, null, null);
 238         mv.visitCode();
 239         if (desc.length() != 1) {
 240             throw new AssertionError("expected single char descriptor: " + desc);
 241         }
 242         switch (desc.charAt(0)) {
 243             case 'J':
 244                 long lvalue = fieldTree.enumConstant().get();
 245                 mv.visitLdcInsn(lvalue);
 246                 mv.visitInsn(LRETURN);
 247                 break;
 248             case 'I':
 249                 int ivalue = fieldTree.enumConstant().get().intValue();
 250                 mv.visitLdcInsn(ivalue);
 251                 mv.visitInsn(IRETURN);
 252                 break;
 253             default:
 254                 throw new AssertionError("should not reach here");
 255         }
 256         mv.visitMaxs(1, 1);
 257         mv.visitEnd();
 258     }
 259 
 260     @Override
 261     public Boolean visitStruct(StructTree structTree, JType jt) {
 262         //generate nested structs recursively
 263         structTree.nestedTypes().forEach(this::generateDecl);
 264 
 265         if (structTree.isAnonymous()) {
 266             //skip anonymous
 267             return false;
 268         }
 269         String nativeName = structTree.name();
 270         Type type = structTree.type();
 271         logger.fine(() -> "Create struct: " + nativeName);
 272 
 273         String intf = Utils.toClassName(nativeName);
 274         String name = headerClassName + "$" + intf;
 275 
 276         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 277         /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
 278          * taking static as a valid flag, so comment this out during development.
 279          */
 280         global_cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 281         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 282         cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
 283                 name, "Ljava/lang/Object;Ljava/foreign/memory/Struct<L" + name + ";>;",
 284                 "java/lang/Object", new String[] {"java/foreign/memory/Struct"});
 285         annotateNativeLocation(cw, structTree);
 286 
 287         AnnotationVisitor av = cw.visitAnnotation(NATIVE_STRUCT, true);
 288         Layout structLayout = structTree.layout(this::decorateAsAccessor);
 289         av.visit("value", structLayout.toString());
 290         av.visitEnd();
 291         cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 292 
 293         // fields
 294         structTree.fields().forEach(fieldTree -> addField(cw, fieldTree, type));
 295         // Write class
 296         cw.visitEnd();
 297         addClassIfNeeded(headerClassName + "$" + intf, cw.toByteArray());



 298         return true;
 299     }
 300 
 301     Layout addGetterSetterName(Layout layout, String accessorName) {
 302         return layout
 303             .withAnnotation("get", accessorName + "$get")
 304             .withAnnotation("set", accessorName + "$set");
 305     }
 306 
 307     Layout decorateAsAccessor(VarTree varTree, Layout layout) {
 308         return addGetterSetterName(layout, varTree.name()).
 309             withAnnotation("ptr", varTree.name() + "$ptr");
 310     }
 311 
 312     Layout decorateAsAccessor(FieldTree fieldTree, Layout layout) {
 313         layout = addGetterSetterName(layout, fieldTree.name());
 314         if (!fieldTree.isBitField()) {
 315             //no pointer accessors for bitfield!
 316             layout = layout.withAnnotation("ptr", fieldTree.name() + "$ptr");
 317         }


 340         String intf = Utils.toClassName(nativeName);
 341         String name = headerClassName + "$" + intf;
 342 
 343         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 344         global_cw.visitInnerClass(name, headerClassName, intf,
 345                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 346         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 347         String[] superAnno = { "java/lang/annotation/Annotation" };
 348         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 349                 name, null, "java/lang/Object", superAnno);
 350         annotateNativeLocation(cw, tree);
 351         AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 352         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 353         av.visitEnd();
 354         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 355         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 356         av.visitEnd();
 357         cw.visitInnerClass(name, headerClassName, intf,
 358                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 359         // Write class
 360         cw.visitEnd();
 361         addClassIfNeeded(headerClassName + "$" + intf, cw.toByteArray());



 362     }
 363 
 364     private void createFunctionalInterface(JType.FunctionalInterfaceType fnif) {
 365         JType.Function fn = fnif.getFunction();
 366         String intf;
 367         String nativeName;
 368         String nDesc = fnif.getFunction().getNativeDescriptor();
 369         intf = fnif.getSimpleName();
 370         nativeName = "anonymous function";
 371         logger.fine(() -> "Create FunctionalInterface " + intf);
 372 
 373         final String name = headerClassName + "$" + intf;
 374 
 375         logger.fine(() -> "Define class " + name + " for native type " + nativeName + nDesc);
 376         global_cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 377         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 378         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 379                 name, "Ljava/lang/Object;",
 380                 "java/lang/Object", new String[0]);
 381         AnnotationVisitor av = cw.visitAnnotation(
 382                 "Ljava/lang/FunctionalInterface;", true);
 383         av.visitEnd();
 384         av = cw.visitAnnotation(NATIVE_CALLBACK, true);
 385         av.visit("value", nDesc);
 386         av.visitEnd();
 387         cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 388 
 389         // add the method
 390 
 391         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 392         if (fn.isVarArgs) {
 393             flags |= ACC_VARARGS;
 394         }
 395         MethodVisitor mv = cw.visitMethod(flags, "fn",
 396                 fn.getDescriptor(), fn.getSignature(false), null);
 397         mv.visitEnd();
 398         // Write class
 399         cw.visitEnd();
 400         addClassIfNeeded(headerClassName + "$" + intf, cw.toByteArray());



 401     }
 402 
 403     @Override
 404     public Boolean visitTypedef(TypedefTree typedefTree, JType jt) {
 405         createAnnotationCls(typedefTree);
 406         return true;
 407     }
 408 
 409     @Override
 410     public Boolean visitTree(Tree tree, JType jt) {
 411         logger.warning(() -> "Unsupported declaration tree:");
 412         logger.warning(() -> tree.toString());
 413         return true;
 414     }
 415 
 416     @Override
 417     public Boolean visitFunction(FunctionTree funcTree, JType jt) {
 418         assert (jt instanceof JType.Function);
 419         JType.Function fn = (JType.Function)jt;
 420         String uniqueName = funcTree.name() + "." + fn.getDescriptor();
 421         if (! global_methods.add(uniqueName)) {
 422             return false; // added already
 423         }
 424         logger.fine(() -> "Add method: " + fn.getSignature(false));
 425         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 426         if (fn.isVarArgs) {
 427             flags |= ACC_VARARGS;
 428         }
 429         MethodVisitor mv = global_cw.visitMethod(flags,
 430                 funcTree.name(), fn.getDescriptor(), fn.getSignature(false), null);
 431         final int arg_cnt = funcTree.numParams();
 432         for (int i = 0; i < arg_cnt; i++) {
 433             String name = funcTree.paramName(i);
 434             final int tmp = i;
 435             logger.finer(() -> "  arg " + tmp + ": " + name);
 436             mv.visitParameter(name, 0);
 437         }
 438 
 439         if (! noNativeLocations) {
 440             AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 441             SourceLocation src = funcTree.location();
 442             SourceLocation.Location loc = src.getFileLocation();
 443             Path p = loc.path();
 444             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 445             av.visit("line", loc.line());
 446             av.visit("column", loc.column());
 447             av.visitEnd();
 448         }
 449 
 450         Type type = funcTree.type();
 451         final String descStr = Utils.getFunction(type).toString();
 452         addHeaderDecl(funcTree.name(), descStr);
 453 
 454         mv.visitEnd();
 455         return true;
 456     }
 457 
 458     private AsmCodeFactory generateDecl(Tree tree) {
 459         try {
 460             logger.fine(() -> "Process tree " + tree.name());
 461             tree.accept(this, tree.isPreprocessing() ? null : headerFile.dictionary().lookup(tree.type()));
 462         } catch (Exception ex) {
 463             handleException(ex);
 464             logger.warning("Tree causing above exception is: " + tree.name());
 465             logger.warning(() -> tree.toString());
 466         }
 467         return this;
 468     }
 469 
 470     @Override
 471     public Boolean visitMacro(MacroTree macroTree, JType jt) {
 472         if (!macroTree.isConstant()) {
 473             logger.fine(() -> "Skipping unrecognized object-like macro " + macroTree.name());
 474             return false;
 475         }
 476         String name = macroTree.name();
 477         Object value = macroTree.value().get();
 478         if (! global_macros.add(name)) {
 479             return false; // added already
 480         }
 481         logger.fine(() -> "Adding macro " + name);
 482         Class<?> macroType = Utils.unboxIfNeeded(value.getClass());
 483 
 484         String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(macroType));
 485         MethodVisitor mv = global_cw.visitMethod(ACC_PUBLIC, name, sig, sig, null);
 486 
 487         if (! noNativeLocations) {
 488             AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 489             SourceLocation src = macroTree.location();
 490             SourceLocation.Location loc = src.getFileLocation();
 491             Path p = loc.path();
 492             av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 493             av.visit("line", loc.line());
 494             av.visit("column", loc.column());
 495             av.visitEnd();
 496         }
 497 
 498         mv.visitCode();
 499 
 500         mv.visitLdcInsn(value);
 501         if (macroType.equals(char.class)) {
 502             mv.visitInsn(I2C);
 503             mv.visitInsn(IRETURN);
 504         } else if (macroType.equals(int.class)) {
 505             mv.visitInsn(IRETURN);
 506         } else if (macroType.equals(float.class)) {
 507             mv.visitInsn(FRETURN);
 508         } else if (macroType.equals(long.class)) {
 509             mv.visitInsn(LRETURN);
 510         } else if (macroType.equals(double.class)) {
 511             mv.visitInsn(DRETURN);
 512         } else if (macroType.equals(String.class)) {
 513             mv.visitInsn(ARETURN);
 514         }
 515         mv.visitMaxs(0, 0);
 516         mv.visitEnd();
 517         return true;
 518     }






























 519 }
< prev index next >