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 jdk.internal.clang.*;
  26 import jdk.internal.clang.Type;
  27 import jdk.internal.org.objectweb.asm.*;
  28 
  29 import java.io.IOException;
  30 import java.nio.file.Path;
  31 import java.nio.file.Paths;
  32 import java.util.Collections;
  33 import java.util.HashMap;
  34 import java.util.HashSet;
  35 import java.util.Map;
  36 import java.util.logging.Logger;
  37 
  38 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  39 
  40 /**
  41  * Scan a header file and generate classes for entities defined in that header
  42  * file.
  43  */
  44 final class AsmCodeFactory extends CodeFactory {
  45     private final Context ctx;
  46     private final ClassWriter global_cw;
  47     private final String internal_name;
  48     private final HeaderFile owner;
  49     private final Map<String, byte[]> types;
  50     private final HashSet<String> handledMacros = new HashSet<>();
  51     private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
  52 
  53     AsmCodeFactory(Context ctx, HeaderFile header) {
  54         this.ctx = ctx;
  55         logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
  56         this.owner = header;
  57         this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName);
  58         this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
  59         this.types = new HashMap<>();
  60         init();
  61     }
  62 
  63     private void init() {
  64         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
  65                 internal_name,
  66                 null, "java/lang/Object", null);
  67         AnnotationVisitor av = global_cw.visitAnnotation(
  68                 "Ljava/nicl/metadata/Header;", true);
  69         av.visit("path", owner.path.toAbsolutePath().toString());
  70         av.visitEnd();
  71         if (owner.libraries != null && !owner.libraries.isEmpty()) {
  72             AnnotationVisitor deps = global_cw.visitAnnotation(
  73                 "Ljava/nicl/metadata/LibraryDependencies;", true);
  74             AnnotationVisitor libNames = deps.visitArray("names");
  75             for (String name : owner.libraries) {
  76                 libNames.visit(null, name);
  77             }
  78             libNames.visitEnd();
  79             if (owner.libraryPaths != null && !owner.libraryPaths.isEmpty()) {
  80                 AnnotationVisitor libPaths = deps.visitArray("paths");
  81                 for (String path : owner.libraryPaths) {
  82                     libPaths.visit(null, path);
  83                 }
  84                 libPaths.visitEnd();
  85             }
  86             deps.visitEnd();
  87         }
  88     }
  89 
  90     private void handleException(Exception ex) {
  91         ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex));
  92         if (Main.DEBUG) {
  93             ex.printStackTrace(ctx.err);
  94         }
  95     }
  96 
  97     private void annotateC(ClassVisitor cw, Cursor dcl) {
  98         AnnotationVisitor av = cw.visitAnnotation(
  99                 "Ljava/nicl/metadata/C;", true);
 100         SourceLocation src = dcl.getSourceLocation();
 101         SourceLocation.Location loc = src.getFileLocation();
 102         Path p = loc.path();
 103         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 104         av.visit("line", loc.line());
 105         av.visit("column", loc.column());
 106         av.visit("USR", dcl.USR());
 107         av.visitEnd();
 108     }
 109 
 110     private void writeClassFile(final ClassWriter cw, String clsName)
 111             throws IOException {
 112         cw.visitEnd();
 113         byte[] bytecodes = cw.toByteArray();
 114         types.put(clsName, bytecodes);
 115     }
 116 
 117     /**
 118      *
 119      * @param cw ClassWriter for the struct
 120      * @param c The FieldDecl cursor
 121      * @param parentType The struct type
 122      */
 123     private void addField(ClassVisitor cw, Cursor c, Type parentType) {
 124         String fieldName = c.spelling();
 125         Type t = c.type();
 126         JType jt = owner.globalLookup(t);
 127         assert (jt != null);
 128         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
 129                 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null);
 130 
 131         AnnotationVisitor av = mv.visitAnnotation(
 132                 "Ljava/nicl/metadata/C;", true);
 133         SourceLocation src = c.getSourceLocation();
 134         SourceLocation.Location loc = src.getFileLocation();
 135         Path p = loc.path();
 136         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 137         av.visit("line", loc.line());
 138         av.visit("column", loc.column());
 139         av.visit("USR", c.USR());
 140         av.visitEnd();
 141 
 142         av = mv.visitAnnotation(
 143                 "Ljava/nicl/metadata/NativeType;", true);
 144         av.visit("layout", Utils.getLayout(t));
 145         av.visit("ctype", t.spelling());
 146         av.visit("size", t.size());
 147         av.visit("name", fieldName);
 148         av.visitEnd();
 149 
 150         if (t.kind() == TypeKind.ConstantArray || t.kind() == TypeKind.IncompleteArray) {
 151             logger.finer(() -> "Array field " + fieldName + ", type " + t.kind().name());
 152             av = mv.visitAnnotation("Ljava/nicl/metadata/Array;", true);
 153             av.visit("elementType", t.getElementType().canonicalType().spelling());
 154             av.visit("elementSize", t.getElementType().canonicalType().size());
 155             if (t.kind() == TypeKind.ConstantArray) {
 156                 av.visit("length", t.getNumberOfElements());
 157             }
 158             av.visitEnd();
 159         }
 160 
 161         if (parentType != null) {
 162             long offset = parentType.getOffsetOf(fieldName);
 163             av = mv.visitAnnotation(
 164                     "Ljava/nicl/metadata/Offset;", true);
 165             av.visit("offset", offset);
 166             if (c.isBitField()) {
 167                 av.visit("bits", c.getBitFieldWidth());
 168             }
 169             av.visitEnd();
 170         }
 171         mv.visitEnd();
 172         cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set",
 173                 "(" + jt.getDescriptor() + ")V", "(" + jt.getSignature() + ")V", null);
 174         // Use long for a reference for now
 175         JType refType = new ReferenceType(jt);
 176         cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ref",
 177                 "()" + refType.getDescriptor(), "()" + refType.getSignature(), null);
 178     }
 179 
 180     private void addConstant(ClassVisitor cw, Cursor c) {
 181         assert (c.kind() == CursorKind.EnumConstantDecl);
 182         String name = c.spelling();
 183         String desc = owner.globalLookup(c.type()).getDescriptor();
 184         Object value = null;
 185         switch (desc) {
 186             case "J":
 187                 value = c.getEnumConstantValue();
 188                 break;
 189             case "I":
 190                 value = (int) c.getEnumConstantValue();
 191                 break;
 192         }
 193         cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, name, desc, null, value);
 194     }
 195 
 196     private void createStruct(Cursor cursor) {
 197         String nativeName = Utils.getIdentifier(cursor);
 198         Type t = cursor.type();
 199         logger.fine(() -> "Create struct: " + nativeName);
 200 
 201         String intf = Utils.toClassName(nativeName);
 202         String name = internal_name + "$" + intf;
 203 
 204         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 205         /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
 206          * taking static as a valid flag, so comment this out during development.
 207          */
 208         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 209         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 210         cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
 211                 name, "Ljava/lang/Object;Ljava/nicl/types/Reference<L" + name + ";>;",
 212                 "java/lang/Object", new String[] {"java/nicl/types/Reference"});
 213         annotateC(cw, cursor);
 214         AnnotationVisitor av = cw.visitAnnotation(
 215                 "Ljava/nicl/metadata/NativeType;", true);
 216         av.visit("layout", Utils.getLayout(t));
 217         av.visit("ctype", t.spelling());
 218         av.visit("size", t.size());
 219         av.visit("isRecordType", true);
 220         av.visitEnd();
 221         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 222 
 223         // fields
 224         Printer dbg = new Printer();
 225         cursor.stream()
 226                 .filter(cx -> cx.kind() == CursorKind.FieldDecl)
 227                 .forEachOrdered(cx -> addField(cw, cx, cursor.type()));
 228         // Write class
 229         try {
 230             writeClassFile(cw, owner.clsName + "$" + intf);
 231         } catch (IOException ex) {
 232             handleException(ex);
 233         }
 234     }
 235 
 236     private void createEnum(Cursor cursor) {
 237         String nativeName = Utils.getIdentifier(cursor);
 238         logger.fine(() -> "create enum: " + nativeName);
 239 
 240         String intf = Utils.toClassName(nativeName);
 241         String name = internal_name + "$" + intf;
 242 
 243         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 244         global_cw.visitInnerClass(name, internal_name, intf,
 245                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 246         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 247         String[] superAnno = { "java/lang/annotation/Annotation" };
 248         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 249                 name, null, "java/lang/Object", superAnno);
 250         annotateC(cw, cursor);
 251         AnnotationVisitor av = cw.visitAnnotation(
 252                 "Ljava/nicl/metadata/NativeType;", true);
 253         Type t = cursor.getEnumDeclIntegerType();
 254         av.visit("layout", Utils.getLayout(t));
 255         av.visit("ctype", t.spelling());
 256         av.visit("size", t.size());
 257         av.visitEnd();
 258 
 259         av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 260         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 261         av.visitEnd();
 262         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 263         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 264         av.visitEnd();
 265         cw.visitInnerClass(name, internal_name, intf,
 266                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 267         // constants
 268         cursor.stream()
 269                 .filter(cx -> cx.kind() == CursorKind.EnumConstantDecl)
 270                 .forEachOrdered(cx -> addConstant(cw, cx));
 271         // Write class
 272         try {
 273             writeClassFile(cw, owner.clsName + "$" + intf);
 274         } catch (IOException ex) {
 275             handleException(ex);
 276         }
 277     }
 278 
 279     private void createAnnotationCls(Cursor dcl) {
 280         String nativeName = Utils.getIdentifier(dcl);
 281         logger.fine(() -> "Create annotation for: " + nativeName);
 282 
 283         String intf = Utils.toClassName(nativeName);
 284         String name = internal_name + "$" + intf;
 285 
 286         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 287         global_cw.visitInnerClass(name, internal_name, intf,
 288                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 289         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 290         String[] superAnno = { "java/lang/annotation/Annotation" };
 291         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 292                 name, null, "java/lang/Object", superAnno);
 293         annotateC(cw, dcl);
 294         Type t = dcl.type().canonicalType();
 295         AnnotationVisitor av = cw.visitAnnotation(
 296                 "Ljava/nicl/metadata/NativeType;", true);
 297         av.visit("layout", Utils.getLayout(t));
 298         av.visit("ctype", t.spelling());
 299         av.visit("size", t.size());
 300         av.visitEnd();
 301 
 302         av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 303         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 304         av.visitEnd();
 305         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 306         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 307         av.visitEnd();
 308         cw.visitInnerClass(name, internal_name, intf,
 309                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 310         // Write class
 311         try {
 312             writeClassFile(cw, owner.clsName + "$" + intf);
 313         } catch (IOException ex) {
 314             handleException(ex);
 315         }
 316     }
 317 
 318     private void createFunctionalInterface(JType2 jt2) {
 319         JType.FnIf fnif = (JType.FnIf) jt2.getDelegate();
 320         JType.Function fn = fnif.getFunction();
 321         String intf = ((JType.InnerType) fnif.type).getName();
 322         logger.fine(() -> "Create FunctionalInterface " + intf);
 323         String nDesc = jt2.getNativeDescriptor();
 324 
 325         final String name = internal_name + "$" + intf;
 326 
 327         logger.fine(() -> "Define class " + name + " for anonymous function " + nDesc);
 328         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 329         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 330         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 331                 name, null, "java/lang/Object", null);
 332         AnnotationVisitor av = cw.visitAnnotation(
 333                 "Ljava/lang/FunctionalInterface;", true);
 334         av.visitEnd();
 335         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 336 
 337         // add the method
 338         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn",
 339                 fn.getDescriptor(), fn.getSignature(), null);
 340         av = mv.visitAnnotation(
 341                 "Ljava/nicl/metadata/NativeType;", true);
 342         av.visit("layout", nDesc);
 343         av.visit("ctype", "N/A");
 344         av.visit("size", -1);
 345         av.visitEnd();
 346         // FIXME: We need calling convention
 347         av = mv.visitAnnotation(
 348                 "Ljava/nicl/metadata/CallingConvention;", true);
 349         av.visit("value", jt2.getCallingConvention());
 350         av.visitEnd();
 351 
 352         mv.visitEnd();
 353         // Write class
 354         try {
 355             writeClassFile(cw, owner.clsName + "$" + intf);
 356         } catch (IOException ex) {
 357             handleException(ex);
 358         }
 359     }
 360 
 361     private void createFunctionalInterface(Cursor dcl, JType.FnIf fnif) {
 362         JType.Function fn = fnif.getFunction();
 363         String intf;
 364         String nativeName;
 365         if (dcl == null) {
 366             intf = ((JType.InnerType) fnif.type).getName();
 367             nativeName = "N/A";
 368         } else {
 369             nativeName = Utils.getIdentifier(dcl);
 370             intf = Utils.toClassName(nativeName);
 371         }
 372         logger.fine(() -> "Create FunctionalInterface " + intf);
 373 
 374         final String name = internal_name + "$" + intf;
 375 
 376         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 377         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 378         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 379         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 380                 name, null, "java/lang/Object", null);
 381         if (dcl != null) {
 382             annotateC(cw, dcl);
 383         }
 384         AnnotationVisitor av = cw.visitAnnotation(
 385                 "Ljava/lang/FunctionalInterface;", true);
 386         av.visitEnd();
 387         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 388 
 389         // add the method
 390         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn",
 391                 fn.getDescriptor(), fn.getSignature(), null);
 392         if (dcl != null) {
 393             av = mv.visitAnnotation(
 394                     "Ljava/nicl/metadata/NativeType;", true);
 395             Type t = dcl.type().canonicalType();
 396             av.visit("layout", Utils.getLayout(t));
 397             av.visit("ctype", t.spelling());
 398             av.visit("size", t.size());
 399             av.visitEnd();
 400             av = mv.visitAnnotation(
 401                     "Ljava/nicl/metadata/CallingConvention;", true);
 402             av.visit("value", t.getCallingConvention().value());
 403             av.visitEnd();
 404         }
 405         mv.visitEnd();
 406         // Write class
 407         try {
 408             writeClassFile(cw, owner.clsName + "$" + intf);
 409         } catch (IOException ex) {
 410             handleException(ex);
 411         }
 412     }
 413 
 414     private void defineType(Cursor dcl, TypeAlias alias) {
 415         if (alias.getAnnotationDescriptor() != null) {
 416             createAnnotationCls(dcl);
 417         } else {
 418             JType real = alias.canonicalType();
 419             if (real instanceof JType.FnIf) {
 420                 createFunctionalInterface(dcl, (JType.FnIf) real);
 421             }
 422             // Otherwise, type alias is a same named stuct
 423         }
 424     }
 425 
 426     private void addMethod(Cursor dcl, JType.Function fn) {
 427         logger.fine(() -> "Add method: " + fn.getSignature());
 428         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 429         if (fn.isVarArgs) {
 430             flags |= ACC_VARARGS;
 431         }
 432         MethodVisitor mv = global_cw.visitMethod(flags,
 433                 dcl.spelling(), fn.getDescriptor(), fn.getSignature(), null);
 434         final int arg_cnt = dcl.numberOfArgs();
 435         for (int i = 0; i < arg_cnt; i++) {
 436             String name = dcl.getArgument(i).spelling();
 437             final int tmp = i;
 438             logger.finer(() -> "  arg " + tmp + ": " + name);
 439             mv.visitParameter(name, 0);
 440         }
 441         AnnotationVisitor av = mv.visitAnnotation(
 442                 "Ljava/nicl/metadata/C;", true);
 443         SourceLocation src = dcl.getSourceLocation();
 444         SourceLocation.Location loc = src.getFileLocation();
 445         Path p = loc.path();
 446         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 447         av.visit("line", loc.line());
 448         av.visit("column", loc.column());
 449         av.visit("USR", dcl.USR());
 450         av.visitEnd();
 451         av = mv.visitAnnotation(
 452                 "Ljava/nicl/metadata/NativeType;", true);
 453         Type t = dcl.type();
 454         av.visit("layout", Utils.getLayout(t));
 455         av.visit("ctype", t.spelling());
 456         av.visit("name", dcl.spelling());
 457         av.visit("size", t.size());
 458         av.visitEnd();
 459         av = mv.visitAnnotation(
 460                 "Ljava/nicl/metadata/CallingConvention;", true);
 461         av.visit("value", t.getCallingConvention().value());
 462         av.visitEnd();
 463 
 464         int idx = 0;
 465         for (JType arg: fn.args) {
 466             if (arg instanceof TypeAlias) {
 467                 TypeAlias alias = (TypeAlias) arg;
 468                 final int tmp = idx;
 469                 logger.finest(() -> "  arg " + tmp + " is an alias " + alias);
 470                 if (alias.getAnnotationDescriptor() != null) {
 471                     mv.visitTypeAnnotation(
 472                             TypeReference.newFormalParameterReference(idx).getValue(),
 473                             null, alias.getAnnotationDescriptor(), true)
 474                       .visitEnd();
 475                 }
 476             }
 477             idx++;
 478         }
 479 
 480         if (fn.returnType instanceof TypeAlias) {
 481             TypeAlias alias = (TypeAlias) fn.returnType;
 482             logger.finest(() -> "  return type is an alias " + alias);
 483             if (alias.getAnnotationDescriptor() != null) {
 484                 mv.visitTypeAnnotation(
 485                         TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue(),
 486                         null, alias.getAnnotationDescriptor(), true)
 487                   .visitEnd();
 488             }
 489         }
 490         mv.visitEnd();
 491     }
 492 
 493     @Override
 494     protected CodeFactory addType(JType jt, Cursor cursor) {
 495         JType2 jt2 = null;
 496         if (jt instanceof JType2) {
 497             jt2 = (JType2) jt;
 498             jt = jt2.getDelegate();
 499         } else {
 500             logger.warning(() -> "Should have JType2 in addType");
 501             if (Main.DEBUG) {
 502                 new Throwable().printStackTrace(ctx.err);
 503             }
 504         }
 505         if (cursor == null) {
 506             assert (jt2 != null);
 507             if (jt instanceof JType.FnIf) {
 508                 createFunctionalInterface(jt2);
 509             }
 510             return this;
 511         }
 512 
 513         try {
 514             logger.fine(() -> "Process cursor " + cursor.spelling());
 515             switch (cursor.kind()) {
 516                 case StructDecl:
 517                     createStruct(cursor);
 518                     break;
 519                 case FunctionDecl:
 520                     assert (jt instanceof JType.Function);
 521                     addMethod(cursor, (JType.Function) jt);
 522                     break;
 523                 case EnumDecl:
 524                     createEnum(cursor);
 525                     break;
 526                 case TypedefDecl:
 527                     // anonymous typedef struct {} xxx will not get TypeAlias
 528                     if (jt instanceof TypeAlias) {
 529                         defineType(cursor, (TypeAlias) jt);
 530                     }
 531                     break;
 532                 case VarDecl:
 533                     addField(global_cw, cursor, null);
 534                     break;
 535                 default:
 536                     logger.warning(() -> "Unsupported declaration Cursor:");
 537                     logger.fine(() -> Printer.Stringifier(p -> p.dumpCursor(cursor, true)));
 538                     break;
 539             }
 540         } catch (Exception ex) {
 541             handleException(ex);
 542             logger.warning("Cursor causing above exception is: " + cursor.spelling());
 543             logger.fine(() -> Printer.Stringifier(p -> p.dumpCursor(cursor, true)));
 544         }
 545         return this;
 546     }
 547 
 548     private static class Literal {
 549         private enum Type {
 550             INT(int.class),
 551             LONG(long.class),
 552             STRING(String.class);
 553 
 554             private Class<?> c;
 555 
 556             Type(Class<?> c) {
 557                 this.c = c;
 558             }
 559 
 560             Class<?> getTypeClass() {
 561                 return c;
 562             }
 563         }
 564 
 565         private final Type type;
 566         private final Object value;
 567 
 568         Literal(Type type, Object value) {
 569             this.type = type;
 570             this.value = value;
 571         }
 572 
 573         Type type() {
 574             return type;
 575         }
 576 
 577         Object getValue() {
 578             return value;
 579         }
 580 
 581         private static Literal parseString(String s) {
 582             if (!s.startsWith("\"") || !s.endsWith("\"")) {
 583                 return null;
 584             }
 585 
 586             return new Literal(Literal.Type.STRING, s.substring(1, s.length() - 1));
 587         }
 588 
 589         private static Literal parseInteger(String s) {
 590             try {
 591                 return new Literal(Literal.Type.INT, Integer.valueOf(s));
 592             } catch (NumberFormatException e) {
 593             }
 594 
 595             if (s.startsWith("0x")) {
 596                 try {
 597                     return new Literal(Literal.Type.INT, Integer.parseInt(s.substring(2), 16));
 598                 } catch (NumberFormatException e) {
 599                 }
 600             }
 601 
 602             return null;
 603         }
 604 
 605         private static Literal parseLong(String s) {
 606             try {
 607                 return new Literal(Literal.Type.LONG, Long.valueOf(s));
 608             } catch (NumberFormatException e) {
 609             }
 610 
 611             if (s.startsWith("0x")) {
 612                 try {
 613                     return new Literal(Literal.Type.LONG, Long.parseLong(s.substring(2), 16));
 614                 } catch (NumberFormatException e) {
 615                 }
 616             }
 617 
 618             String[] ignoredSuffixes = {"L", "UL", "LL"};
 619 
 620             for (String suffix : ignoredSuffixes) {
 621                 if (s.endsWith(suffix)) {
 622                     Literal l = parseLong(s.substring(0, s.length() - suffix.length()));
 623                     if (l != null) {
 624                         return l;
 625                     }
 626                 }
 627             }
 628 
 629             return null;
 630         }
 631 
 632         static Literal parse(String s) {
 633             Literal l;
 634 
 635             l = parseString(s);
 636             if (l != null) {
 637                 return l;
 638             }
 639 
 640             l = parseInteger(s);
 641             if (l != null) {
 642                 return l;
 643             }
 644 
 645             l = parseLong(s);
 646             if (l != null) {
 647                 return l;
 648             }
 649 
 650             return null;
 651         }
 652     }
 653 
 654     @Override
 655     protected CodeFactory addMacro(Cursor cursor) {
 656         String macroName = cursor.spelling();
 657 
 658         if (handledMacros.contains(macroName)) {
 659             logger.fine(() -> "Macro " + macroName + " already handled");
 660             return this;
 661         }
 662 
 663         logger.fine(() -> "Adding macro " + macroName);
 664 
 665         long tu = cursor.getTranslationUnit();
 666         SourceRange range = cursor.getExtent();
 667         String[] tokens = Index.tokenize(tu, range);
 668 
 669         for (String token : tokens) {
 670             logger.finest(() -> "TOKEN: " + token);
 671         }
 672 
 673         if (tokens.length != 2) {
 674             logger.fine(() -> "Skipping macro " + tokens[0] + " because it doesn't have exactly 2 tokens");
 675             return this;
 676         }
 677 
 678         int flags = ACC_PUBLIC;
 679 
 680         Literal l = Literal.parse(tokens[1]);
 681         if (l == null) {
 682             logger.fine(() -> "Skipping macro " + tokens[0] + " because its body isn't a recognizable literal constant");
 683             return this;
 684         }
 685 
 686         String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(l.type().getTypeClass()));
 687         MethodVisitor mv = global_cw.visitMethod(flags, macroName, sig, sig, null);
 688 
 689         AnnotationVisitor av = mv.visitAnnotation("Ljava/nicl/metadata/C;", true);
 690         SourceLocation src = cursor.getSourceLocation();
 691         SourceLocation.Location loc = src.getFileLocation();
 692         Path p = loc.path();
 693         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 694         av.visit("line", loc.line());
 695         av.visit("column", loc.column());
 696         av.visit("USR", cursor.USR());
 697         av.visitEnd();
 698 
 699         mv.visitCode();
 700         mv.visitLdcInsn(l.getValue());
 701         switch (l.type()) {
 702             case INT:
 703                 mv.visitInsn(IRETURN);
 704                 break;
 705             case LONG:
 706                 mv.visitInsn(LRETURN);
 707                 break;
 708             case STRING:
 709                 mv.visitInsn(ARETURN);
 710                 break;
 711         }
 712         mv.visitMaxs(0, 0);
 713         mv.visitEnd();
 714 
 715         handledMacros.add(macroName);
 716 
 717         return this;
 718     }
 719 
 720     @Override
 721     protected void produce() {
 722         try {
 723             writeClassFile(global_cw, owner.clsName);
 724         } catch (IOException ex) {
 725             handleException(ex);
 726         }
 727     }
 728 
 729     @Override
 730     protected Map<String, byte[]> collect() {
 731         // Ensure classes are produced
 732         produce();
 733         HashMap<String, byte[]> rv = new HashMap<>();
 734         // Not copying byte[] for efficiency, perhaps not a safe idea though
 735         if (owner.pkgName.isEmpty()) {
 736             types.forEach((clsName, bytecodes) -> {
 737                 rv.put(clsName, bytecodes);
 738             });
 739         } else {
 740             types.forEach((clsName, bytecodes) -> {
 741                 rv.put(owner.pkgName + "." + clsName, bytecodes);
 742             });
 743         }
 744         return Collections.unmodifiableMap(rv);
 745     }
 746 
 747     public static void main(String[] args) throws IOException {
 748         final Path file = Paths.get(args[1]);
 749         final String pkg = args[0];
 750         Context ctx = new Context();
 751         ctx.usePackageForFolder(file, pkg);
 752         ctx.usePackageForFolder(Paths.get("/usr/include"), "system");
 753         ctx.addSource(file);
 754         ctx.parse();
 755         ctx.collectJarFile(Paths.get(args[2]), pkg);
 756     }
 757 }