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