1 /*
   2  * Copyright (c) 2014, 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.
   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.Libraries;
  27 import java.lang.invoke.MethodHandles;
  28 import java.lang.invoke.MethodHandles.Lookup;
  29 import java.lang.reflect.Method;
  30 import java.foreign.layout.Layout;
  31 import java.nio.file.Path;
  32 import java.nio.file.Paths;
  33 import java.util.ArrayList;
  34 import java.util.Collections;
  35 import java.util.HashMap;
  36 import java.util.HashSet;
  37 import java.util.List;
  38 import java.util.Map;
  39 import java.util.Set;
  40 import java.util.logging.Logger;
  41 import java.util.stream.Stream;
  42 import jdk.internal.clang.SourceLocation;
  43 import jdk.internal.clang.Type;
  44 import jdk.internal.clang.TypeKind;
  45 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  46 import jdk.internal.org.objectweb.asm.FieldVisitor;
  47 import jdk.internal.org.objectweb.asm.ClassVisitor;
  48 import jdk.internal.org.objectweb.asm.ClassWriter;
  49 import jdk.internal.org.objectweb.asm.MethodVisitor;
  50 import jdk.internal.org.objectweb.asm.TypeReference;
  51 import com.sun.tools.jextract.tree.EnumTree;
  52 import com.sun.tools.jextract.tree.FieldTree;
  53 import com.sun.tools.jextract.tree.FunctionTree;
  54 import com.sun.tools.jextract.tree.MacroTree;
  55 import com.sun.tools.jextract.tree.SimpleTreeVisitor;
  56 import com.sun.tools.jextract.tree.StructTree;
  57 import com.sun.tools.jextract.tree.Tree;
  58 import com.sun.tools.jextract.tree.TypedefTree;
  59 import com.sun.tools.jextract.tree.VarTree;
  60 
  61 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT;
  62 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ANNOTATION;
  63 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
  64 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE;
  65 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
  66 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
  67 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
  68 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
  69 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
  70 import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
  71 import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
  72 import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN;
  73 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
  74 import static jdk.internal.org.objectweb.asm.Opcodes.I2C;
  75 import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
  76 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
  77 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
  78 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
  79 import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
  80 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
  81 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
  82 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
  83 
  84 /**
  85  * Scan a header file and generate classes for entities defined in that header
  86  * file.
  87  */
  88 final class AsmCodeFactory extends SimpleTreeVisitor<Void, JType> {
  89     private static final String ANNOTATION_PKG_PREFIX = "Ljava/foreign/annotations/";
  90     private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;";
  91     private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";
  92     private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;";
  93     private static final String NATIVE_STRUCT = ANNOTATION_PKG_PREFIX + "NativeStruct;";
  94 
  95     private final Context ctx;
  96     // static methods forwards for this header file
  97     private final ClassWriter statics_cw;
  98     private final ClassWriter global_cw;
  99     // to avoid duplicate generation of methods, field accessors, macros
 100     private final Set<String> global_methods = new HashSet<>();
 101     private final Set<String> global_fields = new HashSet<>();
 102     private final Set<String> global_macros = new HashSet<>();
 103     private final String internal_name;
 104     private final String internal_name_desc;
 105     private final HeaderFile owner;
 106     private final Map<String, byte[]> types;
 107     private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
 108     private final List<String> headerDeclarations = new ArrayList<>();
 109     private transient boolean built = false;
 110 
 111     // constants used by static forwarder class
 112     private static final String STATICS_CLASS_NAME_SUFFIX = "_h";
 113     private static final String STATICS_LIBRARY_FIELD_NAME = "_theLibrary";
 114 
 115     AsmCodeFactory(Context ctx, HeaderFile header) {
 116         this.ctx = ctx;
 117         logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
 118         this.owner = header;
 119         this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName);
 120         this.internal_name_desc = "L" + internal_name + ";";
 121         this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 122         this.types = new HashMap<>();
 123         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 124                 internal_name,
 125                 null, "java/lang/Object", null);
 126         // if there is no -l option specified, we cannot do Libraries.bind(Lookup, Class).
 127         // so, don't generate statics forwarder.
 128         if (librariesSpecified()) {
 129             this.statics_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
 130             this.statics_cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL,
 131                 internal_name + STATICS_CLASS_NAME_SUFFIX,
 132                 null, "java/lang/Object", null);
 133             staticsInitializer();
 134         } else {
 135             this.statics_cw = null;
 136         }
 137     }
 138 
 139     // emit library interface static field and <clinit> initializer for that field
 140     private void staticsInitializer() {
 141         // library interface field
 142         FieldVisitor fv = statics_cw.visitField(ACC_PRIVATE|ACC_STATIC|ACC_FINAL,
 143             STATICS_LIBRARY_FIELD_NAME, internal_name_desc, null, null);
 144         fv.visitEnd();
 145 
 146         // <clinit> to bind library interface field
 147         MethodVisitor mv = statics_cw.visitMethod(ACC_STATIC,
 148             "<clinit>", "()V", null, null);
 149         mv.visitCode();
 150 
 151         // MethodHandles.lookup()
 152         Method lookupMethod = null;
 153         try {
 154             lookupMethod = MethodHandles.class.getMethod("lookup");
 155         } catch (NoSuchMethodException nsme) {
 156             throw new RuntimeException(nsme);
 157         }
 158         mv.visitMethodInsn(INVOKESTATIC,
 159             jdk.internal.org.objectweb.asm.Type.getInternalName(MethodHandles.class), "lookup",
 160             jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(lookupMethod), false);
 161 
 162         // ldc library-interface-class
 163         mv.visitLdcInsn(jdk.internal.org.objectweb.asm.Type.getObjectType(internal_name));
 164 
 165         // Libraries.bind(lookup, class);
 166         Method bindMethod = null;
 167         try {
 168             bindMethod = Libraries.class.getMethod("bind", Lookup.class, Class.class);
 169         } catch (NoSuchMethodException nsme) {
 170             throw new RuntimeException(nsme);
 171         }
 172         mv.visitMethodInsn(INVOKESTATIC,
 173             jdk.internal.org.objectweb.asm.Type.getInternalName(Libraries.class), "bind",
 174             jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(bindMethod), false);
 175 
 176         // store it in library interface field
 177         mv.visitTypeInsn(CHECKCAST, internal_name);
 178         mv.visitFieldInsn(PUTSTATIC, internal_name + STATICS_CLASS_NAME_SUFFIX,
 179             STATICS_LIBRARY_FIELD_NAME, internal_name_desc);
 180 
 181         mv.visitInsn(RETURN);
 182         mv.visitMaxs(0, 0);
 183         mv.visitEnd();
 184     }
 185 
 186     private boolean librariesSpecified() {
 187         return owner.libraries != null && !owner.libraries.isEmpty();
 188     }
 189 
 190     private void generateNativeHeader() {
 191         AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
 192         av.visit("path", owner.path.toAbsolutePath().toString());
 193         if (librariesSpecified()) {
 194             AnnotationVisitor libNames = av.visitArray("libraries");
 195             for (String name : owner.libraries) {
 196                 libNames.visit(null, name);
 197             }
 198             libNames.visitEnd();
 199             if (owner.libraryPaths != null && !owner.libraryPaths.isEmpty()) {
 200                 AnnotationVisitor libPaths = av.visitArray("libraryPaths");
 201                 for (String path : owner.libraryPaths) {
 202                     libPaths.visit(null, path);
 203                 }
 204                 libPaths.visitEnd();
 205             }
 206         }
 207         av.visit("declarations", String.join(" ", headerDeclarations));
 208         av.visitEnd();
 209     }
 210 
 211     private void handleException(Exception ex) {
 212         ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex));
 213         if (Main.DEBUG) {
 214             ex.printStackTrace(ctx.err);
 215         }
 216     }
 217 
 218     private void annotateNativeLocation(ClassVisitor cw, Tree tree) {
 219         AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true);
 220         SourceLocation src = tree.location();
 221         SourceLocation.Location loc = src.getFileLocation();
 222         Path p = loc.path();
 223         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 224         av.visit("line", loc.line());
 225         av.visit("column", loc.column());
 226         av.visit("USR", tree.USR());
 227         av.visitEnd();
 228     }
 229 
 230     private void writeClassFile(final ClassWriter cw, String clsName)
 231             throws IOException {
 232         cw.visitEnd();
 233         byte[] bytecodes = cw.toByteArray();
 234         if (null != types.put(clsName, bytecodes)) {
 235             logger.warning("Class " + clsName + " definition is overwritten");
 236         }
 237     }
 238 
 239     private static boolean isBitField(Tree tree) {
 240         return tree instanceof FieldTree && ((FieldTree)tree).isBitField();
 241     }
 242 
 243     /**
 244      *
 245      * @param cw ClassWriter for the struct
 246      * @param tree The Tree
 247      * @param parentType The struct type
 248      */
 249     private void addField(ClassVisitor cw, Tree tree, Type parentType) {
 250         String fieldName = tree.name();
 251         assert !fieldName.isEmpty();
 252         Type type = tree.type();
 253         JType jt = owner.globalLookup(type);
 254         assert (jt != null);
 255         if (cw == global_cw) {
 256             String uniqueName = fieldName + "." + jt.getDescriptor();
 257             if (! global_fields.add(uniqueName)) {
 258                 return; // added already
 259             }
 260         }
 261         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
 262                 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null);
 263 
 264         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 265         SourceLocation src = tree.location();
 266         SourceLocation.Location loc = src.getFileLocation();
 267         Path p = loc.path();
 268         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 269         av.visit("line", loc.line());
 270         av.visit("column", loc.column());
 271         av.visit("USR", tree.USR());
 272         av.visitEnd();
 273 
 274         mv.visitEnd();
 275         cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set",
 276                 "(" + jt.getDescriptor() + ")V",
 277                 "(" + JType.getPointerVoidAsWildcard(jt) + ")V", null);
 278         if (tree instanceof VarTree || !isBitField(tree)) {
 279             JType ptrType = new PointerType(jt);
 280             cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr",
 281                     "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(), null);
 282         }
 283     }
 284 
 285     @Override
 286     public Void visitVar(VarTree varTree, JType jt) {
 287         addField(global_cw, varTree, null);
 288         Layout layout = varTree.layout();
 289         String descStr = decorateAsAccessor(varTree, layout).toString();
 290         addHeaderDecl(varTree.name(), descStr);
 291         return null;
 292     }
 293 
 294     private void addHeaderDecl(String symbol, String desc) {
 295         headerDeclarations.add(String.format("%s=%s", symbol, desc));
 296     }
 297 
 298     private void addConstant(ClassWriter cw, FieldTree fieldTree) {
 299         assert (fieldTree.isEnumConstant());
 300         String name = fieldTree.name();
 301         String desc = owner.globalLookup(fieldTree.type()).getDescriptor();
 302         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, "()" + desc, null, null);
 303         mv.visitCode();
 304         if (desc.length() != 1) {
 305             throw new AssertionError("expected single char descriptor: " + desc);
 306         }
 307         switch (desc.charAt(0)) {
 308             case 'J':
 309                 long lvalue = fieldTree.enumConstant().get();
 310                 mv.visitLdcInsn(lvalue);
 311                 mv.visitInsn(LRETURN);
 312                 break;
 313             case 'I':
 314                 int ivalue = fieldTree.enumConstant().get().intValue();
 315                 mv.visitLdcInsn(ivalue);
 316                 mv.visitInsn(IRETURN);
 317                 break;
 318             default:
 319                 throw new AssertionError("should not reach here");
 320         }
 321         mv.visitMaxs(1, 1);
 322         mv.visitEnd();
 323         // static forwarder method for this enum constant
 324         emitStaticsForwarder(name, "()" + desc, null);
 325     }
 326 
 327     @Override
 328     public Void visitStruct(StructTree structTree, JType jt) {
 329         String nativeName = structTree.name();
 330         Type type = structTree.type();
 331         logger.fine(() -> "Create struct: " + nativeName);
 332 
 333         String intf = Utils.toClassName(nativeName);
 334         String name = internal_name + "$" + intf;
 335 
 336         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 337         /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
 338          * taking static as a valid flag, so comment this out during development.
 339          */
 340         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 341         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 342         cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
 343                 name, "Ljava/lang/Object;Ljava/foreign/memory/Struct<L" + name + ";>;",
 344                 "java/lang/Object", new String[] {"java/foreign/memory/Struct"});
 345         annotateNativeLocation(cw, structTree);
 346 
 347         AnnotationVisitor av = cw.visitAnnotation(NATIVE_STRUCT, true);
 348         Layout structLayout = structTree.layout(this::decorateAsAccessor);
 349         av.visit("value", structLayout.toString());
 350         av.visitEnd();
 351         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 352 
 353         // fields
 354         structTree.fields().forEach(fieldTree -> addField(cw, fieldTree, type));
 355         // Write class
 356         try {
 357             writeClassFile(cw, owner.clsName + "$" + intf);
 358         } catch (IOException ex) {
 359             handleException(ex);
 360         }
 361         return null;
 362     }
 363 
 364     Layout addGetterSetterName(Layout layout, String accessorName) {
 365         return layout
 366             .withAnnotation("get", accessorName + "$get")
 367             .withAnnotation("set", accessorName + "$set");
 368     }
 369 
 370     Layout decorateAsAccessor(VarTree varTree, Layout layout) {
 371         return addGetterSetterName(layout, varTree.name()).
 372             withAnnotation("ptr", varTree.name() + "$ptr");
 373     }
 374 
 375     Layout decorateAsAccessor(FieldTree fieldTree, Layout layout) {
 376         layout = addGetterSetterName(layout, fieldTree.name());
 377         if (!fieldTree.isBitField()) {
 378             //no pointer accessors for bitfield!
 379             layout = layout.withAnnotation("ptr", fieldTree.name() + "$ptr");
 380         }
 381         return layout;
 382     }
 383 
 384     @Override
 385     public Void visitEnum(EnumTree enumTree, JType jt) {
 386         // define enum constants in global_cw
 387         enumTree.constants().forEach(constant -> addConstant(global_cw, constant));
 388 
 389         if (enumTree.name().isEmpty()) {
 390             // We are done with anonymous enum
 391             return null;
 392         }
 393 
 394         // generate annotation class for named enum
 395         createAnnotationCls(enumTree);
 396         return null;
 397     }
 398 
 399     private void createAnnotationCls(Tree tree) {
 400         String nativeName = tree.name();
 401         logger.fine(() -> "Create annotation for: " + nativeName);
 402 
 403         String intf = Utils.toClassName(nativeName);
 404         String name = internal_name + "$" + intf;
 405 
 406         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 407         global_cw.visitInnerClass(name, internal_name, intf,
 408                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 409         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 410         String[] superAnno = { "java/lang/annotation/Annotation" };
 411         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 412                 name, null, "java/lang/Object", superAnno);
 413         annotateNativeLocation(cw, tree);
 414         Type type = tree.type().canonicalType();
 415         AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 416         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 417         av.visitEnd();
 418         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 419         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 420         av.visitEnd();
 421         cw.visitInnerClass(name, internal_name, intf,
 422                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 423         // Write class
 424         try {
 425             writeClassFile(cw, owner.clsName + "$" + intf);
 426         } catch (IOException ex) {
 427             handleException(ex);
 428         }
 429     }
 430 
 431     private void createFunctionalInterface(Tree tree, JType.FnIf fnif) {
 432         JType.Function fn = fnif.getFunction();
 433         String intf;
 434         String nativeName;
 435         String nDesc = fnif.getFunction().getNativeDescriptor();
 436         if (tree == null) {
 437             intf = ((JType.InnerType) fnif.type).getName();
 438             nativeName = "anonymous function";
 439         } else {
 440             nativeName = tree.name();
 441             intf = Utils.toClassName(nativeName);
 442         }
 443         logger.fine(() -> "Create FunctionalInterface " + intf);
 444 
 445         final String name = internal_name + "$" + intf;
 446 
 447         logger.fine(() -> "Define class " + name + " for native type " + nativeName + nDesc);
 448         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 449         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 450         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 451                 name, "Ljava/lang/Object;",
 452                 "java/lang/Object", new String[0]);
 453         if (tree != null) {
 454             annotateNativeLocation(cw, tree);
 455         }
 456         AnnotationVisitor av = cw.visitAnnotation(
 457                 "Ljava/lang/FunctionalInterface;", true);
 458         av.visitEnd();
 459         av = cw.visitAnnotation(NATIVE_CALLBACK, true);
 460         av.visit("value", nDesc);
 461         av.visitEnd();
 462         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 463 
 464         // add the method
 465 
 466         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 467         if (fn.isVarArgs) {
 468             flags |= ACC_VARARGS;
 469         }
 470         MethodVisitor mv = cw.visitMethod(flags, "fn",
 471                 fn.getDescriptor(), fn.getSignature(), null);
 472         mv.visitEnd();
 473         // Write class
 474         try {
 475             writeClassFile(cw, owner.clsName + "$" + intf);
 476         } catch (IOException ex) {
 477             handleException(ex);
 478         }
 479     }
 480 
 481     @Override
 482     public Void visitTypedef(TypedefTree typedefTree, JType jt) {
 483         // anonymous typedef struct {} xxx will not get TypeAlias
 484         if (jt instanceof TypeAlias) {
 485             TypeAlias alias = (TypeAlias) jt;
 486             if (alias.getAnnotationDescriptor() != null) {
 487                 createAnnotationCls(typedefTree);
 488             } else {
 489                 JType real = alias.canonicalType();
 490                 if (real instanceof JType.FnIf) {
 491                     createFunctionalInterface(typedefTree, (JType.FnIf) real);
 492                 }
 493                 // Otherwise, type alias is a same named stuct
 494             }
 495         }
 496         return null;
 497     }
 498 
 499     @Override
 500     public Void visitTree(Tree tree, JType jt) {
 501         logger.warning(() -> "Unsupported declaration tree:");
 502         logger.warning(() -> tree.toString());
 503         return null;
 504     }
 505 
 506     @Override
 507     public Void visitFunction(FunctionTree funcTree, JType jt) {
 508         assert (jt instanceof JType.Function);
 509         JType.Function fn = (JType.Function)jt;
 510         String uniqueName = funcTree.name() + "." + fn.getDescriptor();
 511         if (! global_methods.add(uniqueName)) {
 512             return null; // added already
 513         }
 514         logger.fine(() -> "Add method: " + fn.getSignature());
 515         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 516         if (fn.isVarArgs) {
 517             flags |= ACC_VARARGS;
 518         }
 519         MethodVisitor mv = global_cw.visitMethod(flags,
 520                 funcTree.name(), fn.getDescriptor(), fn.getSignature(), null);
 521         final int arg_cnt = funcTree.numParams();
 522         for (int i = 0; i < arg_cnt; i++) {
 523             String name = funcTree.paramName(i);
 524             final int tmp = i;
 525             logger.finer(() -> "  arg " + tmp + ": " + name);
 526             mv.visitParameter(name, 0);
 527         }
 528         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 529         SourceLocation src = funcTree.location();
 530         SourceLocation.Location loc = src.getFileLocation();
 531         Path p = loc.path();
 532         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 533         av.visit("line", loc.line());
 534         av.visit("column", loc.column());
 535         av.visit("USR", funcTree.USR());
 536         av.visitEnd();
 537         Type type = funcTree.type();
 538         final String descStr = Utils.getFunction(type).toString();
 539         addHeaderDecl(funcTree.name(), descStr);
 540 
 541         int idx = 0;
 542         for (JType arg: fn.args) {
 543             if (arg instanceof TypeAlias) {
 544                 TypeAlias alias = (TypeAlias) arg;
 545                 final int tmp = idx;
 546                 logger.finest(() -> "  arg " + tmp + " is an alias " + alias);
 547                 if (alias.getAnnotationDescriptor() != null) {
 548                     mv.visitTypeAnnotation(
 549                             TypeReference.newFormalParameterReference(idx).getValue(),
 550                             null, alias.getAnnotationDescriptor(), true)
 551                       .visitEnd();
 552                 }
 553             }
 554             idx++;
 555         }
 556 
 557         if (fn.returnType instanceof TypeAlias) {
 558             TypeAlias alias = (TypeAlias) fn.returnType;
 559             logger.finest(() -> "  return type is an alias " + alias);
 560             if (alias.getAnnotationDescriptor() != null) {
 561                 mv.visitTypeAnnotation(
 562                         TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue(),
 563                         null, alias.getAnnotationDescriptor(), true)
 564                   .visitEnd();
 565             }
 566         }
 567         mv.visitEnd();
 568         emitStaticsForwarder(funcTree.name(), fn.getDescriptor(), fn.getSignature());
 569         return null;
 570     }
 571 
 572     // emit static forwarder method for a specific library interface method
 573     private void emitStaticsForwarder(String name, String desc, String signature) {
 574         if (statics_cw == null) {
 575             return;
 576         }
 577         MethodVisitor statics_mv = statics_cw.visitMethod(ACC_PUBLIC | ACC_STATIC,
 578             name, desc, signature, null);
 579         statics_mv.visitCode();
 580 
 581         // load library interface (static) field
 582         statics_mv.visitFieldInsn(GETSTATIC, internal_name + STATICS_CLASS_NAME_SUFFIX,
 583             STATICS_LIBRARY_FIELD_NAME, internal_name_desc);
 584 
 585         // forward the call to the interface
 586         jdk.internal.org.objectweb.asm.Type[] argTypes = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(desc);
 587         jdk.internal.org.objectweb.asm.Type retType = jdk.internal.org.objectweb.asm.Type.getReturnType(desc);
 588 
 589         int loadIdx = 0;
 590         for (int i = 0; i < argTypes.length; i++) {
 591             statics_mv.visitVarInsn(argTypes[i].getOpcode(ILOAD), loadIdx);
 592             loadIdx += argTypes[i].getSize();
 593         }
 594         statics_mv.visitMethodInsn(INVOKEINTERFACE, internal_name, name, desc, true);
 595         statics_mv.visitInsn(retType.getOpcode(IRETURN));
 596 
 597         statics_mv.visitMaxs(0, 0);
 598         statics_mv.visitEnd();
 599     }
 600 
 601     protected AsmCodeFactory addType(JType jt, Tree tree) {
 602         JType2 jt2 = null;
 603         if (jt instanceof JType2) {
 604             jt2 = (JType2) jt;
 605             jt = jt2.getDelegate();
 606         } else {
 607             logger.warning(() -> "Should have JType2 in addType");
 608             if (Main.DEBUG) {
 609                 new Throwable().printStackTrace(ctx.err);
 610             }
 611         }
 612         if (tree == null) {
 613             assert (jt2 != null);
 614             if (jt instanceof JType.FnIf) {
 615                 createFunctionalInterface(null, (JType.FnIf) jt);
 616             }
 617             return this;
 618         }
 619         /*
 620         // FIXME: what is this?
 621         boolean noDef = cursor.isInvalid();
 622         if (noDef) {
 623             cursor = jt2.getCursor();
 624         }
 625         */
 626 
 627         try {
 628             logger.fine(() -> "Process tree " + tree.name());
 629             tree.accept(this, jt);
 630         } catch (Exception ex) {
 631             handleException(ex);
 632             logger.warning("Tree causing above exception is: " + tree.name());
 633             logger.warning(() -> tree.toString());
 634         }
 635         return this;
 636     }
 637 
 638     @Override
 639     public Void visitMacro(MacroTree macroTree, JType jt) {
 640         if (!macroTree.isConstant()) {
 641             logger.fine(() -> "Skipping unrecognized object-like macro " + macroTree.name());
 642             return null;
 643         }
 644         String name = macroTree.name();
 645         Object value = macroTree.value().get();
 646         if (! global_macros.add(name)) {
 647             return null; // added already
 648         }
 649         logger.fine(() -> "Adding macro " + name);
 650         Class<?> macroType = Utils.unboxIfNeeded(value.getClass());
 651 
 652         String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(macroType));
 653         MethodVisitor mv = global_cw.visitMethod(ACC_PUBLIC, name, sig, sig, null);
 654 
 655         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 656         SourceLocation src = macroTree.location();
 657         SourceLocation.Location loc = src.getFileLocation();
 658         Path p = loc.path();
 659         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 660         av.visit("line", loc.line());
 661         av.visit("column", loc.column());
 662         av.visit("USR", macroTree.USR());
 663         av.visitEnd();
 664 
 665         mv.visitCode();
 666 
 667         mv.visitLdcInsn(value);
 668         if (macroType.equals(char.class)) {
 669             mv.visitInsn(I2C);
 670             mv.visitInsn(IRETURN);
 671         } else if (macroType.equals(int.class)) {
 672             mv.visitInsn(IRETURN);
 673         } else if (macroType.equals(float.class)) {
 674             mv.visitInsn(FRETURN);
 675         } else if (macroType.equals(long.class)) {
 676             mv.visitInsn(LRETURN);
 677         } else if (macroType.equals(double.class)) {
 678             mv.visitInsn(DRETURN);
 679         } else if (macroType.equals(String.class)) {
 680             mv.visitInsn(ARETURN);
 681         }
 682         mv.visitMaxs(0, 0);
 683         mv.visitEnd();
 684         // static forwarder method for this macro
 685         emitStaticsForwarder(name, sig, sig);
 686         return null;
 687     }
 688 
 689     protected synchronized void produce() {
 690         if (built) {
 691             throw new IllegalStateException("Produce is called multiple times");
 692         }
 693         built = true;
 694         generateNativeHeader();
 695         try {
 696             writeClassFile(global_cw, owner.clsName);
 697             if (statics_cw != null) {
 698                 writeClassFile(statics_cw, owner.clsName + STATICS_CLASS_NAME_SUFFIX);
 699             }
 700         } catch (IOException ex) {
 701             handleException(ex);
 702         }
 703     }
 704 
 705     protected Map<String, byte[]> collect() {
 706         // Ensure classes are produced
 707         if (!built) produce();
 708         HashMap<String, byte[]> rv = new HashMap<>();
 709         // Not copying byte[] for efficiency, perhaps not a safe idea though
 710         if (owner.pkgName.isEmpty()) {
 711             types.forEach((clsName, bytecodes) -> {
 712                 rv.put(clsName, bytecodes);
 713             });
 714         } else {
 715             types.forEach((clsName, bytecodes) -> {
 716                 rv.put(owner.pkgName + "." + clsName, bytecodes);
 717             });
 718         }
 719         return Collections.unmodifiableMap(rv);
 720     }
 721 }