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