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         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
  95                 internal_name,
  96                 null, "java/lang/Object", null);




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



 560         generateNativeHeader();
 561         try {
 562             writeClassFile(global_cw, owner.clsName);
 563         } catch (IOException ex) {
 564             handleException(ex);
 565         }
 566     }
 567 
 568     protected Map<String, byte[]> collect() {
 569         // Ensure classes are produced
 570         if (!built) produce();
 571         HashMap<String, byte[]> rv = new HashMap<>();
 572         // Not copying byte[] for efficiency, perhaps not a safe idea though
 573         if (owner.pkgName.isEmpty()) {
 574             types.forEach((clsName, bytecodes) -> {
 575                 rv.put(clsName, bytecodes);
 576             });
 577         } else {
 578             types.forEach((clsName, bytecodes) -> {
 579                 rv.put(owner.pkgName + "." + clsName, bytecodes);
 580             });
 581         }
 582         return Collections.unmodifiableMap(rv);
 583     }
 584 
 585     public static void main(String[] args) throws IOException {
 586         final Path file = Paths.get(args[1]);
 587         final String pkg = args[0];
 588         Context ctx = new Context();
 589         ctx.usePackageForFolder(file, pkg);
 590         ctx.usePackageForFolder(Paths.get("/usr/include"), "system");
 591         ctx.addSource(file);
 592         ctx.parse();
 593         ctx.collectJarFile(Paths.get(args[2]), args, pkg);
 594     }
 595 }
--- EOF ---