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         if (fieldName.isEmpty()) {
 170             //skip anon fields
 171             return;
 172         }
 173         Type type = tree.type();
 174         JType jt = owner.globalLookup(type);
 175         assert (jt != null);
 176         if (cw == global_cw) {
 177             String uniqueName = fieldName + "." + jt.getDescriptor();
 178             if (! global_fields.add(uniqueName)) {
 179                 return; // added already
 180             }
 181         }
 182         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
 183                 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null);
 184 
 185         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 186         SourceLocation src = tree.location();
 187         SourceLocation.Location loc = src.getFileLocation();
 188         Path p = loc.path();
 189         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 190         av.visit("line", loc.line());
 191         av.visit("column", loc.column());
 192         av.visit("USR", tree.USR());
 193         av.visitEnd();
 194 
 195         mv.visitEnd();
 196         cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set",
 197                 "(" + jt.getDescriptor() + ")V",
 198                 "(" + JType.getPointerVoidAsWildcard(jt) + ")V", null);
 199         if (tree instanceof VarTree || !isBitField(tree)) {
 200             JType ptrType = new PointerType(jt);
 201             cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr",
 202                     "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(), null);
 203         }
 204     }
 205 
 206     @Override
 207     public Void visitVar(VarTree varTree, JType jt) {
 208         addField(global_cw, varTree, null);
 209         Layout layout = varTree.layout();
 210         String descStr = decorateAsAccessor(varTree, layout).toString();
 211         addHeaderDecl(varTree.name(), descStr);
 212         return null;
 213     }
 214 
 215     private void addHeaderDecl(String symbol, String desc) {
 216         headerDeclarations.add(String.format("%s=%s", symbol, desc));
 217     }
 218 
 219     private void addConstant(ClassVisitor cw, FieldTree fieldTree) {
 220         assert (fieldTree.isEnumConstant());
 221         String name = fieldTree.name();
 222         String desc = owner.globalLookup(fieldTree.type()).getDescriptor();
 223         Object value = null;
 224         switch (desc) {
 225             case "J":
 226                 value = fieldTree.enumConstant().get();
 227                 break;
 228             case "I":
 229                 value = fieldTree.enumConstant().get().intValue();
 230                 break;
 231         }
 232         cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, name, desc, null, value);
 233     }
 234 
 235     @Override
 236     public Void visitStruct(StructTree structTree, JType jt) {
 237         String nativeName = structTree.identifier();
 238         Type type = structTree.type();
 239         logger.fine(() -> "Create struct: " + nativeName);
 240 
 241         String intf = Utils.toClassName(nativeName);
 242         String name = internal_name + "$" + intf;
 243 
 244         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 245         /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
 246          * taking static as a valid flag, so comment this out during development.
 247          */
 248         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 249         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 250         cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
 251                 name, "Ljava/lang/Object;Ljava/foreign/memory/Struct<L" + name + ";>;",
 252                 "java/lang/Object", new String[] {"java/foreign/memory/Struct"});
 253         annotateNativeLocation(cw, structTree);
 254 
 255         AnnotationVisitor av = cw.visitAnnotation(NATIVE_STRUCT, true);
 256         Layout structLayout = structTree.layout(this::decorateAsAccessor);
 257         av.visit("value", structLayout.toString());
 258         av.visitEnd();
 259         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 260 
 261         // fields
 262         structTree.fields().forEach(fieldTree -> addField(cw, fieldTree, type));
 263         // Write class
 264         try {
 265             writeClassFile(cw, owner.clsName + "$" + intf);
 266         } catch (IOException ex) {
 267             handleException(ex);
 268         }
 269         return null;
 270     }
 271 
 272     Layout addGetterSetterName(Layout layout, String accessorName) {
 273         return layout
 274             .withAnnotation("get", accessorName + "$get")
 275             .withAnnotation("set", accessorName + "$set");
 276     }
 277 
 278     Layout decorateAsAccessor(VarTree varTree, Layout layout) {
 279         return addGetterSetterName(layout, varTree.name()).
 280             withAnnotation("ptr", varTree.name() + "$ptr");
 281     }
 282 
 283     Layout decorateAsAccessor(FieldTree fieldTree, Layout layout) {
 284         layout = addGetterSetterName(layout, fieldTree.name());
 285         if (!fieldTree.isBitField()) {
 286             //no pointer accessors for bitfield!
 287             layout = layout.withAnnotation("ptr", fieldTree.name() + "$ptr");
 288         }
 289         return layout;
 290     }
 291 
 292     @Override
 293     public Void visitEnum(EnumTree enumTree, JType jt) {
 294         // define enum constants in global_cw
 295         enumTree.constants().forEach(constant -> addConstant(global_cw, constant));
 296 
 297         if (enumTree.name().isEmpty()) {
 298             // We are done with anonymous enum
 299             return null;
 300         }
 301 
 302         // generate annotation class for named enum
 303         createAnnotationCls(enumTree);
 304         return null;
 305     }
 306 
 307     private void createAnnotationCls(Tree tree) {
 308         String nativeName = tree.identifier();
 309         logger.fine(() -> "Create annotation for: " + nativeName);
 310 
 311         String intf = Utils.toClassName(nativeName);
 312         String name = internal_name + "$" + intf;
 313 
 314         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 315         global_cw.visitInnerClass(name, internal_name, intf,
 316                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 317         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 318         String[] superAnno = { "java/lang/annotation/Annotation" };
 319         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 320                 name, null, "java/lang/Object", superAnno);
 321         annotateNativeLocation(cw, tree);
 322         Type type = tree.type().canonicalType();
 323         AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 324         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 325         av.visitEnd();
 326         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 327         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 328         av.visitEnd();
 329         cw.visitInnerClass(name, internal_name, intf,
 330                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 331         // Write class
 332         try {
 333             writeClassFile(cw, owner.clsName + "$" + intf);
 334         } catch (IOException ex) {
 335             handleException(ex);
 336         }
 337     }
 338 
 339     private void createFunctionalInterface(Tree tree, JType.FnIf fnif) {
 340         JType.Function fn = fnif.getFunction();
 341         String intf;
 342         String nativeName;
 343         String nDesc = fnif.getFunction().getNativeDescriptor();
 344         if (tree == null) {
 345             intf = ((JType.InnerType) fnif.type).getName();
 346             nativeName = "anonymous function";
 347         } else {
 348             nativeName = tree.identifier();
 349             intf = Utils.toClassName(nativeName);
 350         }
 351         logger.fine(() -> "Create FunctionalInterface " + intf);
 352 
 353         final String name = internal_name + "$" + intf;
 354 
 355         logger.fine(() -> "Define class " + name + " for native type " + nativeName + nDesc);
 356         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 357         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 358         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 359                 name, "Ljava/lang/Object;Ljava/foreign/memory/Callback<L" + name + ";>;",
 360                 "java/lang/Object", new String[] {"java/foreign/memory/Callback"});
 361         if (tree != null) {
 362             annotateNativeLocation(cw, tree);
 363         }
 364         AnnotationVisitor av = cw.visitAnnotation(
 365                 "Ljava/lang/FunctionalInterface;", true);
 366         av.visitEnd();
 367         av = cw.visitAnnotation(NATIVE_CALLBACK, true);
 368         av.visit("value", nDesc);
 369         av.visitEnd();
 370         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 371 
 372         // add the method
 373 
 374         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 375         if (fn.isVarArgs) {
 376             flags |= ACC_VARARGS;
 377         }
 378         MethodVisitor mv = cw.visitMethod(flags, "fn",
 379                 fn.getDescriptor(), fn.getSignature(), null);
 380         mv.visitEnd();
 381         // Write class
 382         try {
 383             writeClassFile(cw, owner.clsName + "$" + intf);
 384         } catch (IOException ex) {
 385             handleException(ex);
 386         }
 387     }
 388 
 389     @Override
 390     public Void visitTypedef(TypedefTree typedefTree, JType jt) {
 391         Type t = typedefTree.type();
 392         if (t.canonicalType().kind() == TypeKind.Enum &&
 393             t.spelling().equals(t.canonicalType().getDeclarationCursor().spelling())) {
 394             logger.fine("Skip redundant typedef " + t.spelling());
 395             return null;
 396         }
 397 
 398         // anonymous typedef struct {} xxx will not get TypeAlias
 399         if (jt instanceof TypeAlias) {
 400             TypeAlias alias = (TypeAlias) jt;
 401             if (alias.getAnnotationDescriptor() != null) {
 402                 createAnnotationCls(typedefTree);
 403             } else {
 404                 JType real = alias.canonicalType();
 405                 if (real instanceof JType.FnIf) {
 406                     createFunctionalInterface(typedefTree, (JType.FnIf) real);
 407                 }
 408                 // Otherwise, type alias is a same named stuct
 409             }
 410         }
 411         return null;
 412     }
 413 
 414     @Override
 415     public Void visitTree(Tree tree, JType jt) {
 416         logger.warning(() -> "Unsupported declaration tree:");
 417         logger.warning(() -> tree.toString());
 418         return null;
 419     }
 420 
 421     @Override
 422     public Void visitFunction(FunctionTree funcTree, JType jt) {
 423         assert (jt instanceof JType.Function);
 424         JType.Function fn = (JType.Function)jt;
 425         String uniqueName = funcTree.name() + "." + fn.getDescriptor();
 426         if (! global_methods.add(uniqueName)) {
 427             return null; // added already
 428         }
 429         logger.fine(() -> "Add method: " + fn.getSignature());
 430         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 431         if (fn.isVarArgs) {
 432             flags |= ACC_VARARGS;
 433         }
 434         MethodVisitor mv = global_cw.visitMethod(flags,
 435                 funcTree.name(), fn.getDescriptor(), fn.getSignature(), null);
 436         final int arg_cnt = funcTree.numParams();
 437         for (int i = 0; i < arg_cnt; i++) {
 438             String name = funcTree.paramName(i);
 439             final int tmp = i;
 440             logger.finer(() -> "  arg " + tmp + ": " + name);
 441             mv.visitParameter(name, 0);
 442         }
 443         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 444         SourceLocation src = funcTree.location();
 445         SourceLocation.Location loc = src.getFileLocation();
 446         Path p = loc.path();
 447         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 448         av.visit("line", loc.line());
 449         av.visit("column", loc.column());
 450         av.visit("USR", funcTree.USR());
 451         av.visitEnd();
 452         Type type = funcTree.type();
 453         final String descStr = Utils.getFunction(type).toString();
 454         addHeaderDecl(funcTree.name(), descStr);
 455 
 456         int idx = 0;
 457         for (JType arg: fn.args) {
 458             if (arg instanceof TypeAlias) {
 459                 TypeAlias alias = (TypeAlias) arg;
 460                 final int tmp = idx;
 461                 logger.finest(() -> "  arg " + tmp + " is an alias " + alias);
 462                 if (alias.getAnnotationDescriptor() != null) {
 463                     mv.visitTypeAnnotation(
 464                             TypeReference.newFormalParameterReference(idx).getValue(),
 465                             null, alias.getAnnotationDescriptor(), true)
 466                       .visitEnd();
 467                 }
 468             }
 469             idx++;
 470         }
 471 
 472         if (fn.returnType instanceof TypeAlias) {
 473             TypeAlias alias = (TypeAlias) fn.returnType;
 474             logger.finest(() -> "  return type is an alias " + alias);
 475             if (alias.getAnnotationDescriptor() != null) {
 476                 mv.visitTypeAnnotation(
 477                         TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue(),
 478                         null, alias.getAnnotationDescriptor(), true)
 479                   .visitEnd();
 480             }
 481         }
 482         mv.visitEnd();
 483         return null;
 484     }
 485 
 486     protected AsmCodeFactory addType(JType jt, Tree tree) {
 487         JType2 jt2 = null;
 488         if (jt instanceof JType2) {
 489             jt2 = (JType2) jt;
 490             jt = jt2.getDelegate();
 491         } else {
 492             logger.warning(() -> "Should have JType2 in addType");
 493             if (Main.DEBUG) {
 494                 new Throwable().printStackTrace(ctx.err);
 495             }
 496         }
 497         if (tree == null) {
 498             assert (jt2 != null);
 499             if (jt instanceof JType.FnIf) {
 500                 createFunctionalInterface(null, (JType.FnIf) jt);
 501             }
 502             return this;
 503         }
 504         /*
 505         // FIXME: what is this?
 506         boolean noDef = cursor.isInvalid();
 507         if (noDef) {
 508             cursor = jt2.getCursor();
 509         }
 510         */
 511 
 512         try {
 513             logger.fine(() -> "Process tree " + tree.name());
 514             tree.accept(this, jt);
 515         } catch (Exception ex) {
 516             handleException(ex);
 517             logger.warning("Tree causing above exception is: " + tree.name());
 518             logger.warning(() -> tree.toString());
 519         }
 520         return this;
 521     }
 522 
 523     @Override
 524     public Void visitMacro(MacroTree macroTree, JType jt) {
 525         if (!macroTree.isConstant()) {
 526             logger.fine(() -> "Skipping unrecognized object-like macro " + macroTree.name());
 527             return null;
 528         }
 529         String name = macroTree.name();
 530         Object value = macroTree.value().get();
 531         if (! global_macros.add(name)) {
 532             return null; // added already
 533         }
 534         logger.fine(() -> "Adding macro " + name);
 535         Class<?> macroType = (Class<?>) Util.unboxIfNeeded(value.getClass());
 536 
 537         String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(macroType));
 538         MethodVisitor mv = global_cw.visitMethod(ACC_PUBLIC, name, sig, sig, null);
 539 
 540         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 541         SourceLocation src = macroTree.location();
 542         SourceLocation.Location loc = src.getFileLocation();
 543         Path p = loc.path();
 544         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 545         av.visit("line", loc.line());
 546         av.visit("column", loc.column());
 547         av.visit("USR", macroTree.USR());
 548         av.visitEnd();
 549 
 550         mv.visitCode();
 551 
 552         mv.visitLdcInsn(value);
 553         if (macroType.equals(char.class)) {
 554             mv.visitInsn(I2C);
 555             mv.visitInsn(IRETURN);
 556         } else if (macroType.equals(int.class)) {
 557             mv.visitInsn(IRETURN);
 558         } else if (macroType.equals(float.class)) {
 559             mv.visitInsn(FRETURN);
 560         } else if (macroType.equals(long.class)) {
 561             mv.visitInsn(LRETURN);
 562         } else if (macroType.equals(double.class)) {
 563             mv.visitInsn(DRETURN);
 564         } else if (macroType.equals(String.class)) {
 565             mv.visitInsn(ARETURN);
 566         }
 567         mv.visitMaxs(0, 0);
 568         mv.visitEnd();
 569         return null;
 570     }
 571 
 572     protected synchronized void produce() {
 573         if (built) {
 574             throw new IllegalStateException("Produce is called multiple times");
 575         }
 576         built = true;
 577         generateNativeHeader();
 578         try {
 579             writeClassFile(global_cw, owner.clsName);
 580         } catch (IOException ex) {
 581             handleException(ex);
 582         }
 583     }
 584 
 585     protected Map<String, byte[]> collect() {
 586         // Ensure classes are produced
 587         if (!built) produce();
 588         HashMap<String, byte[]> rv = new HashMap<>();
 589         // Not copying byte[] for efficiency, perhaps not a safe idea though
 590         if (owner.pkgName.isEmpty()) {
 591             types.forEach((clsName, bytecodes) -> {
 592                 rv.put(clsName, bytecodes);
 593             });
 594         } else {
 595             types.forEach((clsName, bytecodes) -> {
 596                 rv.put(owner.pkgName + "." + clsName, bytecodes);
 597             });
 598         }
 599         return Collections.unmodifiableMap(rv);
 600     }
 601 }