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