< prev index next >

src/jdk.jextract/share/classes/com/sun/tools/jextract/AsmCodeFactory.java

Print this page




  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 C = ANNOTATION_PKG_PREFIX + "C;";
  49     private static final String CALLING_CONVENTION = ANNOTATION_PKG_PREFIX + "CallingConvention;";
  50     private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";

  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 
  62     AsmCodeFactory(Context ctx, HeaderFile header) {
  63         this.ctx = ctx;
  64         logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
  65         this.owner = header;
  66         this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName);
  67         this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
  68         this.types = new HashMap<>();
  69         init();
  70     }
  71 
  72     private void init() {
  73         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
  74                 internal_name,
  75                 null, "java/lang/Object", null);



  76         AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
  77         av.visit("headerPath", 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.visitEnd();
  93     }
  94 
  95     private void handleException(Exception ex) {
  96         ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex));
  97         if (Main.DEBUG) {
  98             ex.printStackTrace(ctx.err);
  99         }
 100     }
 101 
 102     private void annotateC(ClassVisitor cw, Cursor dcl) {
 103         AnnotationVisitor av = cw.visitAnnotation(C, true);
 104         SourceLocation src = dcl.getSourceLocation();
 105         SourceLocation.Location loc = src.getFileLocation();
 106         Path p = loc.path();
 107         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 108         av.visit("line", loc.line());
 109         av.visit("column", loc.column());
 110         av.visit("USR", dcl.USR());
 111         av.visitEnd();
 112     }
 113 
 114     private void writeClassFile(final ClassWriter cw, String clsName)
 115             throws IOException {
 116         cw.visitEnd();
 117         byte[] bytecodes = cw.toByteArray();
 118         if (null != types.put(clsName, bytecodes)) {
 119             logger.warning("Class " + clsName + " definition is overwritten");
 120         }
 121     }
 122 
 123     /**
 124      *
 125      * @param cw ClassWriter for the struct
 126      * @param c The FieldDecl cursor
 127      * @param parentType The struct type
 128      */
 129     private void addField(ClassVisitor cw, Cursor c, Type parentType) {
 130         String fieldName = c.spelling();
 131         Type t = c.type();
 132         JType jt = owner.globalLookup(t);
 133         assert (jt != null);
 134         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
 135                 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null);
 136 
 137         AnnotationVisitor av = mv.visitAnnotation(C, true);
 138         SourceLocation src = c.getSourceLocation();
 139         SourceLocation.Location loc = src.getFileLocation();
 140         Path p = loc.path();
 141         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 142         av.visit("line", loc.line());
 143         av.visit("column", loc.column());
 144         av.visit("USR", c.USR());
 145         av.visitEnd();
 146 
 147         av = mv.visitAnnotation(NATIVE_TYPE, true);
 148         av.visit("layout", Utils.getLayout(t));
 149         av.visit("ctype", t.spelling());
 150         av.visit("size", t.size());
 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());


 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         annotateC(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.visit("isRecordType", true);
 221         av.visitEnd();
 222         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 223 
 224         // fields
 225         Printer dbg = new Printer();
 226         structFields(cursor).forEachOrdered(cx -> addField(cw, cx, cursor.type()));
 227         // Write class
 228         try {
 229             writeClassFile(cw, owner.clsName + "$" + intf);
 230         } catch (IOException ex) {
 231             handleException(ex);
 232         }
 233     }
 234 
 235     // A stream of fields of a struct (or union). Note that we have to include
 236     // fields from nested annoymous unions and structs in the containing struct.
 237     private Stream<Cursor> structFields(Cursor cursor) {
 238         return cursor.children()
 239             .flatMap(c -> c.isAnonymousStruct()? structFields(c) : Stream.of(c))
 240             .filter(c -> c.kind() == CursorKind.FieldDecl);


 252         }
 253 
 254         // generate annotation class for named enum
 255         createAnnotationCls(cursor);
 256     }
 257 
 258     private void createAnnotationCls(Cursor dcl) {
 259         String nativeName = Utils.getIdentifier(dcl);
 260         logger.fine(() -> "Create annotation for: " + nativeName);
 261 
 262         String intf = Utils.toClassName(nativeName);
 263         String name = internal_name + "$" + intf;
 264 
 265         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 266         global_cw.visitInnerClass(name, internal_name, intf,
 267                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 268         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 269         String[] superAnno = { "java/lang/annotation/Annotation" };
 270         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 271                 name, null, "java/lang/Object", superAnno);
 272         annotateC(cw, dcl);
 273         Type t = dcl.type().canonicalType();
 274         AnnotationVisitor av = cw.visitAnnotation(NATIVE_TYPE, true);
 275         av.visit("layout", Utils.getLayout(t));
 276         av.visit("ctype", t.spelling());
 277         av.visit("size", t.size());
 278         av.visitEnd();
 279 
 280         av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 281         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 282         av.visitEnd();
 283         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 284         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 285         av.visitEnd();
 286         cw.visitInnerClass(name, internal_name, intf,
 287                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 288         // Write class
 289         try {
 290             writeClassFile(cw, owner.clsName + "$" + intf);
 291         } catch (IOException ex) {
 292             handleException(ex);
 293         }
 294     }
 295 
 296     private void createFunctionalInterface(JType2 jt2) {
 297         JType.FnIf fnif = (JType.FnIf) jt2.getDelegate();
 298         JType.Function fn = fnif.getFunction();
 299         String intf = ((JType.InnerType) fnif.type).getName();
 300         logger.fine(() -> "Create FunctionalInterface " + intf);
 301         String nDesc = jt2.getNativeDescriptor();
 302 
 303         final String name = internal_name + "$" + intf;
 304 
 305         logger.fine(() -> "Define class " + name + " for anonymous function " + nDesc);
 306         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 307         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 308         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 309                 name, null, "java/lang/Object", null);
 310         AnnotationVisitor av = cw.visitAnnotation(
 311                 "Ljava/lang/FunctionalInterface;", true);
 312         av.visitEnd();



 313         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 314 
 315         // add the method
 316         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn",
 317                 fn.getDescriptor(), fn.getSignature(), null);
 318         av = mv.visitAnnotation(NATIVE_TYPE, true);
 319         av.visit("layout", nDesc);
 320         av.visit("ctype", "N/A");
 321         av.visit("size", -1);
 322         av.visitEnd();
 323         // FIXME: We need calling convention
 324         av = mv.visitAnnotation(CALLING_CONVENTION, true);
 325         av.visit("value", jt2.getCallingConvention());
 326         av.visitEnd();
 327 
 328         mv.visitEnd();
 329         // Write class
 330         try {
 331             writeClassFile(cw, owner.clsName + "$" + intf);
 332         } catch (IOException ex) {
 333             handleException(ex);
 334         }
 335     }
 336 
 337     private void createFunctionalInterface(Cursor dcl, JType.FnIf fnif) {
 338         JType.Function fn = fnif.getFunction();
 339         String intf;
 340         String nativeName;
 341         if (dcl == null) {
 342             intf = ((JType.InnerType) fnif.type).getName();
 343             nativeName = "N/A";
 344         } else {
 345             nativeName = Utils.getIdentifier(dcl);
 346             intf = Utils.toClassName(nativeName);
 347         }
 348         logger.fine(() -> "Create FunctionalInterface " + intf);
 349 
 350         final String name = internal_name + "$" + intf;
 351 
 352         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 353         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 354         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 355         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 356                 name, null, "java/lang/Object", null);
 357         if (dcl != null) {
 358             annotateC(cw, dcl);
 359         }
 360         AnnotationVisitor av = cw.visitAnnotation(
 361                 "Ljava/lang/FunctionalInterface;", true);
 362         av.visitEnd();






 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         if (dcl != null) {
 369             av = mv.visitAnnotation(NATIVE_TYPE, true);
 370             Type t = dcl.type().canonicalType();
 371             av.visit("layout", Utils.getLayout(t));
 372             av.visit("ctype", t.spelling());
 373             av.visit("size", t.size());
 374             av.visitEnd();
 375             av = mv.visitAnnotation(CALLING_CONVENTION, true);
 376             av.visit("value", t.getCallingConvention().value());
 377             av.visitEnd();
 378         }
 379         mv.visitEnd();
 380         // Write class
 381         try {
 382             writeClassFile(cw, owner.clsName + "$" + intf);
 383         } catch (IOException ex) {
 384             handleException(ex);
 385         }
 386     }
 387 
 388     private void defineType(Cursor dcl, TypeAlias alias) {
 389         if (alias.getAnnotationDescriptor() != null) {
 390             createAnnotationCls(dcl);
 391         } else {
 392             JType real = alias.canonicalType();
 393             if (real instanceof JType.FnIf) {
 394                 createFunctionalInterface(dcl, (JType.FnIf) real);
 395             }
 396             // Otherwise, type alias is a same named stuct
 397         }
 398     }
 399 
 400     private void addMethod(Cursor dcl, JType.Function fn) {
 401         logger.fine(() -> "Add method: " + fn.getSignature());
 402         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 403         if (fn.isVarArgs) {
 404             flags |= ACC_VARARGS;
 405         }
 406         MethodVisitor mv = global_cw.visitMethod(flags,
 407                 dcl.spelling(), fn.getDescriptor(), fn.getSignature(), null);
 408         final int arg_cnt = dcl.numberOfArgs();
 409         for (int i = 0; i < arg_cnt; i++) {
 410             String name = dcl.getArgument(i).spelling();
 411             final int tmp = i;
 412             logger.finer(() -> "  arg " + tmp + ": " + name);
 413             mv.visitParameter(name, 0);
 414         }
 415         AnnotationVisitor av = mv.visitAnnotation(C, true);
 416         SourceLocation src = dcl.getSourceLocation();
 417         SourceLocation.Location loc = src.getFileLocation();
 418         Path p = loc.path();
 419         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 420         av.visit("line", loc.line());
 421         av.visit("column", loc.column());
 422         av.visit("USR", dcl.USR());
 423         av.visitEnd();
 424         av = mv.visitAnnotation(NATIVE_TYPE, true);
 425         Type t = dcl.type();
 426         av.visit("layout", Utils.getLayout(t));

 427         av.visit("ctype", t.spelling());
 428         av.visit("name", dcl.spelling());
 429         av.visit("size", t.size());
 430         av.visitEnd();
 431         av = mv.visitAnnotation(CALLING_CONVENTION, true);
 432         av.visit("value", t.getCallingConvention().value());
 433         av.visitEnd();
 434 





 435         int idx = 0;
 436         for (JType arg: fn.args) {
 437             if (arg instanceof TypeAlias) {
 438                 TypeAlias alias = (TypeAlias) arg;
 439                 final int tmp = idx;
 440                 logger.finest(() -> "  arg " + tmp + " is an alias " + alias);
 441                 if (alias.getAnnotationDescriptor() != null) {
 442                     mv.visitTypeAnnotation(
 443                             TypeReference.newFormalParameterReference(idx).getValue(),
 444                             null, alias.getAnnotationDescriptor(), true)
 445                       .visitEnd();
 446                 }
 447             }
 448             idx++;
 449         }
 450 
 451         if (fn.returnType instanceof TypeAlias) {
 452             TypeAlias alias = (TypeAlias) fn.returnType;
 453             logger.finest(() -> "  return type is an alias " + alias);
 454             if (alias.getAnnotationDescriptor() != null) {


 641         for (String token : tokens) {
 642             logger.finest(() -> "TOKEN: " + token);
 643         }
 644 
 645         if (tokens.length != 2) {
 646             logger.fine(() -> "Skipping macro " + tokens[0] + " because it doesn't have exactly 2 tokens");
 647             return this;
 648         }
 649 
 650         int flags = ACC_PUBLIC;
 651 
 652         Literal l = Literal.parse(tokens[1]);
 653         if (l == null) {
 654             logger.fine(() -> "Skipping macro " + tokens[0] + " because its body isn't a recognizable literal constant");
 655             return this;
 656         }
 657 
 658         String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(l.type().getTypeClass()));
 659         MethodVisitor mv = global_cw.visitMethod(flags, macroName, sig, sig, null);
 660 
 661         AnnotationVisitor av = mv.visitAnnotation(C, true);
 662         SourceLocation src = cursor.getSourceLocation();
 663         SourceLocation.Location loc = src.getFileLocation();
 664         Path p = loc.path();
 665         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 666         av.visit("line", loc.line());
 667         av.visit("column", loc.column());
 668         av.visit("USR", cursor.USR());
 669         av.visitEnd();
 670 
 671         mv.visitCode();
 672         mv.visitLdcInsn(l.getValue());
 673         switch (l.type()) {
 674             case INT:
 675                 mv.visitInsn(IRETURN);
 676                 break;
 677             case LONG:
 678                 mv.visitInsn(LRETURN);
 679                 break;
 680             case STRING:
 681                 mv.visitInsn(ARETURN);
 682                 break;
 683         }
 684         mv.visitMaxs(0, 0);
 685         mv.visitEnd();
 686 
 687         handledMacros.add(macroName);
 688 
 689         return this;
 690     }
 691 
 692     @Override
 693     protected void produce() {

 694         try {
 695             writeClassFile(global_cw, owner.clsName);
 696         } catch (IOException ex) {
 697             handleException(ex);
 698         }
 699     }
 700 
 701     @Override
 702     protected Map<String, byte[]> collect() {
 703         // Ensure classes are produced
 704         produce();
 705         HashMap<String, byte[]> rv = new HashMap<>();
 706         // Not copying byte[] for efficiency, perhaps not a safe idea though
 707         if (owner.pkgName.isEmpty()) {
 708             types.forEach((clsName, bytecodes) -> {
 709                 rv.put(clsName, bytecodes);
 710             });
 711         } else {
 712             types.forEach((clsName, bytecodes) -> {
 713                 rv.put(owner.pkgName + "." + clsName, bytecodes);


  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());


 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);


 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) {


 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);
< prev index next >