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