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