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