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