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