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