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 java.io.IOException;
  26 import java.foreign.layout.Layout;
  27 import java.nio.file.Path;
  28 import java.nio.file.Paths;
  29 import java.util.ArrayList;
  30 import java.util.Collections;
  31 import java.util.HashMap;
  32 import java.util.HashSet;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.Set;
  36 import java.util.logging.Logger;
  37 import java.util.stream.Stream;
  38 import jdk.internal.clang.SourceLocation;
  39 import jdk.internal.clang.Type;
  40 import jdk.internal.clang.TypeKind;
  41 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  42 import jdk.internal.org.objectweb.asm.ClassVisitor;
  43 import jdk.internal.org.objectweb.asm.ClassWriter;
  44 import jdk.internal.org.objectweb.asm.MethodVisitor;
  45 import jdk.internal.org.objectweb.asm.TypeReference;
  46 import com.sun.tools.jextract.tree.EnumTree;
  47 import com.sun.tools.jextract.tree.FieldTree;
  48 import com.sun.tools.jextract.tree.FunctionTree;
  49 import com.sun.tools.jextract.tree.MacroTree;
  50 import com.sun.tools.jextract.tree.SimpleTreeVisitor;
  51 import com.sun.tools.jextract.tree.StructTree;
  52 import com.sun.tools.jextract.tree.Tree;
  53 import com.sun.tools.jextract.tree.TypedefTree;
  54 import com.sun.tools.jextract.tree.VarTree;
  55 
  56 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT;
  57 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ANNOTATION;
  58 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
  59 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE;
  60 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
  61 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
  62 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
  63 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
  64 import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
  65 import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN;
  66 import static jdk.internal.org.objectweb.asm.Opcodes.I2C;
  67 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
  68 import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
  69 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
  70 
  71 /**
  72  * Scan a header file and generate classes for entities defined in that header
  73  * file.
  74  */
  75 final class AsmCodeFactory extends SimpleTreeVisitor<Void, JType> {
  76     private static final String ANNOTATION_PKG_PREFIX = "Ljava/foreign/annotations/";
  77     private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;";
  78     private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";
  79     private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;";
  80     private static final String NATIVE_STRUCT = ANNOTATION_PKG_PREFIX + "NativeStruct;";
  81 
  82     private final Context ctx;
  83     private final ClassWriter global_cw;
  84     // to avoid duplicate generation of methods, field accessors, macros
  85     private final Set<String> global_methods = new HashSet<>();
  86     private final Set<String> global_fields = new HashSet<>();
  87     private final Set<String> global_macros = new HashSet<>();
  88     private final String internal_name;
  89     private final HeaderFile owner;
  90     private final Map<String, byte[]> types;
  91     private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
  92     private final List<String> headerDeclarations = new ArrayList<>();
  93     private final StaticForwarderGenerator staticForwardGen;
  94 
  95     private transient boolean built = false;
  96 
  97     AsmCodeFactory(Context ctx, HeaderFile header) {
  98         this.ctx = ctx;
  99         logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
 100         this.owner = header;
 101         this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName);
 102         this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 103         this.types = new HashMap<>();
 104         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 105                 internal_name,
 106                 null, "java/lang/Object", null);
 107         if (ctx.getGenStaticForwarder()) {
 108             this.staticForwardGen = new StaticForwarderGenerator(header);
 109         } else {
 110             this.staticForwardGen = null;
 111         }
 112     }
 113 
 114     private void generateNativeHeader() {
 115         AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
 116         av.visit("path", owner.path.toAbsolutePath().toString());
 117         if (owner.libraries != null && !owner.libraries.isEmpty()) {
 118             AnnotationVisitor libNames = av.visitArray("libraries");
 119             for (String name : owner.libraries) {
 120                 libNames.visit(null, name);
 121             }
 122             libNames.visitEnd();
 123             if (owner.libraryPaths != null && !owner.libraryPaths.isEmpty()) {
 124                 AnnotationVisitor libPaths = av.visitArray("libraryPaths");
 125                 for (String path : owner.libraryPaths) {
 126                     libPaths.visit(null, path);
 127                 }
 128                 libPaths.visitEnd();
 129             }
 130         }
 131         av.visit("declarations", String.join(" ", headerDeclarations));
 132         av.visitEnd();
 133     }
 134 
 135     private void handleException(Exception ex) {
 136         ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex));
 137         if (Main.DEBUG) {
 138             ex.printStackTrace(ctx.err);
 139         }
 140     }
 141 
 142     private void annotateNativeLocation(ClassVisitor cw, Tree tree) {
 143         AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true);
 144         SourceLocation src = tree.location();
 145         SourceLocation.Location loc = src.getFileLocation();
 146         Path p = loc.path();
 147         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 148         av.visit("line", loc.line());
 149         av.visit("column", loc.column());
 150         av.visit("USR", tree.USR());
 151         av.visitEnd();
 152     }
 153 
 154     private void writeClassFile(final ClassWriter cw, String clsName)
 155             throws IOException {
 156         cw.visitEnd();
 157         byte[] bytecodes = cw.toByteArray();
 158         if (null != types.put(clsName, bytecodes)) {
 159             logger.warning("Class " + clsName + " definition is overwritten");
 160         }
 161     }
 162 
 163     private static boolean isBitField(Tree tree) {
 164         return tree instanceof FieldTree && ((FieldTree)tree).isBitField();
 165     }
 166 
 167     /**
 168      *
 169      * @param cw ClassWriter for the struct
 170      * @param tree The Tree
 171      * @param parentType The struct type
 172      */
 173     private void addField(ClassVisitor cw, Tree tree, Type parentType) {
 174         String fieldName = tree.name();
 175         assert !fieldName.isEmpty();
 176         Type type = tree.type();
 177         JType jt = owner.globalLookup(type);
 178         assert (jt != null);
 179         if (cw == global_cw) {
 180             String uniqueName = fieldName + "." + jt.getDescriptor();
 181             if (! global_fields.add(uniqueName)) {
 182                 return; // added already
 183             }
 184         }
 185         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
 186                 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null);
 187 
 188         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 189         SourceLocation src = tree.location();
 190         SourceLocation.Location loc = src.getFileLocation();
 191         Path p = loc.path();
 192         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 193         av.visit("line", loc.line());
 194         av.visit("column", loc.column());
 195         av.visit("USR", tree.USR());
 196         av.visitEnd();
 197 
 198         mv.visitEnd();
 199         mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set",
 200                 "(" + jt.getDescriptor() + ")V",
 201                 "(" + JType.getPointerVoidAsWildcard(jt) + ")V", null);
 202         mv.visitEnd();
 203         if (tree instanceof VarTree || !isBitField(tree)) {
 204             JType ptrType = new PointerType(jt);
 205             mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr",
 206                     "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(), null);
 207             mv.visitEnd();
 208         }
 209     }
 210 
 211     @Override
 212     public Void visitVar(VarTree varTree, JType jt) {
 213         addField(global_cw, varTree, null);
 214         Layout layout = varTree.layout();
 215         String descStr = decorateAsAccessor(varTree, layout).toString();
 216         addHeaderDecl(varTree.name(), descStr);
 217         if (staticForwardGen != null) {
 218             staticForwardGen.visitVar(varTree, jt);
 219         }
 220         return null;
 221     }
 222 
 223     private void addHeaderDecl(String symbol, String desc) {
 224         headerDeclarations.add(String.format("%s=%s", symbol, desc));
 225     }
 226 
 227     private void addConstant(ClassWriter cw, FieldTree fieldTree) {
 228         assert (fieldTree.isEnumConstant());
 229         String name = fieldTree.name();
 230         String desc = owner.globalLookup(fieldTree.type()).getDescriptor();
 231         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, "()" + desc, null, null);
 232         mv.visitCode();
 233         if (desc.length() != 1) {
 234             throw new AssertionError("expected single char descriptor: " + desc);
 235         }
 236         switch (desc.charAt(0)) {
 237             case 'J':
 238                 long lvalue = fieldTree.enumConstant().get();
 239                 mv.visitLdcInsn(lvalue);
 240                 mv.visitInsn(LRETURN);
 241                 break;
 242             case 'I':
 243                 int ivalue = fieldTree.enumConstant().get().intValue();
 244                 mv.visitLdcInsn(ivalue);
 245                 mv.visitInsn(IRETURN);
 246                 break;
 247             default:
 248                 throw new AssertionError("should not reach here");
 249         }
 250         mv.visitMaxs(1, 1);
 251         mv.visitEnd();
 252     }
 253 
 254     @Override
 255     public Void visitStruct(StructTree structTree, JType jt) {
 256         String nativeName = structTree.name();
 257         Type type = structTree.type();
 258         logger.fine(() -> "Create struct: " + nativeName);
 259 
 260         String intf = Utils.toClassName(nativeName);
 261         String name = internal_name + "$" + intf;
 262 
 263         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 264         /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
 265          * taking static as a valid flag, so comment this out during development.
 266          */
 267         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 268         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 269         cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
 270                 name, "Ljava/lang/Object;Ljava/foreign/memory/Struct<L" + name + ";>;",
 271                 "java/lang/Object", new String[] {"java/foreign/memory/Struct"});
 272         annotateNativeLocation(cw, structTree);
 273 
 274         AnnotationVisitor av = cw.visitAnnotation(NATIVE_STRUCT, true);
 275         Layout structLayout = structTree.layout(this::decorateAsAccessor);
 276         av.visit("value", structLayout.toString());
 277         av.visitEnd();
 278         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 279 
 280         // fields
 281         structTree.fields().forEach(fieldTree -> addField(cw, fieldTree, type));
 282         // Write class
 283         try {
 284             writeClassFile(cw, owner.clsName + "$" + intf);
 285         } catch (IOException ex) {
 286             handleException(ex);
 287         }
 288         return null;
 289     }
 290 
 291     Layout addGetterSetterName(Layout layout, String accessorName) {
 292         return layout
 293             .withAnnotation("get", accessorName + "$get")
 294             .withAnnotation("set", accessorName + "$set");
 295     }
 296 
 297     Layout decorateAsAccessor(VarTree varTree, Layout layout) {
 298         return addGetterSetterName(layout, varTree.name()).
 299             withAnnotation("ptr", varTree.name() + "$ptr");
 300     }
 301 
 302     Layout decorateAsAccessor(FieldTree fieldTree, Layout layout) {
 303         layout = addGetterSetterName(layout, fieldTree.name());
 304         if (!fieldTree.isBitField()) {
 305             //no pointer accessors for bitfield!
 306             layout = layout.withAnnotation("ptr", fieldTree.name() + "$ptr");
 307         }
 308         return layout;
 309     }
 310 
 311     @Override
 312     public Void visitEnum(EnumTree enumTree, JType jt) {
 313         // define enum constants in global_cw
 314         enumTree.constants().forEach(constant -> addConstant(global_cw, constant));
 315 
 316         if (enumTree.name().isEmpty()) {
 317             // We are done with anonymous enum
 318             return null;
 319         }
 320 
 321         // generate annotation class for named enum
 322         createAnnotationCls(enumTree);
 323         if (staticForwardGen != null) {
 324             staticForwardGen.visitEnum(enumTree, jt);
 325         }
 326         return null;
 327     }
 328 
 329     private void createAnnotationCls(Tree tree) {
 330         String nativeName = tree.name();
 331         logger.fine(() -> "Create annotation for: " + nativeName);
 332 
 333         String intf = Utils.toClassName(nativeName);
 334         String name = internal_name + "$" + intf;
 335 
 336         logger.fine(() -> "Define class " + name + " for native type " + nativeName);
 337         global_cw.visitInnerClass(name, internal_name, intf,
 338                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 339         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 340         String[] superAnno = { "java/lang/annotation/Annotation" };
 341         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
 342                 name, null, "java/lang/Object", superAnno);
 343         annotateNativeLocation(cw, tree);
 344         Type type = tree.type().canonicalType();
 345         AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
 346         av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
 347         av.visitEnd();
 348         av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
 349         av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
 350         av.visitEnd();
 351         cw.visitInnerClass(name, internal_name, intf,
 352                 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
 353         // Write class
 354         try {
 355             writeClassFile(cw, owner.clsName + "$" + intf);
 356         } catch (IOException ex) {
 357             handleException(ex);
 358         }
 359     }
 360 
 361     private void createFunctionalInterface(Tree tree, JType.FnIf fnif) {
 362         JType.Function fn = fnif.getFunction();
 363         String intf;
 364         String nativeName;
 365         String nDesc = fnif.getFunction().getNativeDescriptor();
 366         if (tree == null) {
 367             intf = ((JType.InnerType) fnif.type).getName();
 368             nativeName = "anonymous function";
 369         } else {
 370             nativeName = tree.name();
 371             intf = Utils.toClassName(nativeName);
 372         }
 373         logger.fine(() -> "Create FunctionalInterface " + intf);
 374 
 375         final String name = internal_name + "$" + intf;
 376 
 377         logger.fine(() -> "Define class " + name + " for native type " + nativeName + nDesc);
 378         global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 379         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 380         cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
 381                 name, "Ljava/lang/Object;",
 382                 "java/lang/Object", new String[0]);
 383         if (tree != null) {
 384             annotateNativeLocation(cw, tree);
 385         }
 386         AnnotationVisitor av = cw.visitAnnotation(
 387                 "Ljava/lang/FunctionalInterface;", true);
 388         av.visitEnd();
 389         av = cw.visitAnnotation(NATIVE_CALLBACK, true);
 390         av.visit("value", nDesc);
 391         av.visitEnd();
 392         cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
 393 
 394         // add the method
 395 
 396         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 397         if (fn.isVarArgs) {
 398             flags |= ACC_VARARGS;
 399         }
 400         MethodVisitor mv = cw.visitMethod(flags, "fn",
 401                 fn.getDescriptor(), fn.getSignature(), null);
 402         mv.visitEnd();
 403         // Write class
 404         try {
 405             writeClassFile(cw, owner.clsName + "$" + intf);
 406         } catch (IOException ex) {
 407             handleException(ex);
 408         }
 409     }
 410 
 411     @Override
 412     public Void visitTypedef(TypedefTree typedefTree, JType jt) {
 413         // anonymous typedef struct {} xxx will not get TypeAlias
 414         if (jt instanceof TypeAlias) {
 415             TypeAlias alias = (TypeAlias) jt;
 416             if (alias.getAnnotationDescriptor() != null) {
 417                 createAnnotationCls(typedefTree);
 418             } else {
 419                 JType real = alias.canonicalType();
 420                 if (real instanceof JType.FnIf) {
 421                     createFunctionalInterface(typedefTree, (JType.FnIf) real);
 422                 }
 423                 // Otherwise, type alias is a same named stuct
 424             }
 425         }
 426         return null;
 427     }
 428 
 429     @Override
 430     public Void visitTree(Tree tree, JType jt) {
 431         logger.warning(() -> "Unsupported declaration tree:");
 432         logger.warning(() -> tree.toString());
 433         return null;
 434     }
 435 
 436     @Override
 437     public Void visitFunction(FunctionTree funcTree, JType jt) {
 438         assert (jt instanceof JType.Function);
 439         JType.Function fn = (JType.Function)jt;
 440         String uniqueName = funcTree.name() + "." + fn.getDescriptor();
 441         if (! global_methods.add(uniqueName)) {
 442             return null; // added already
 443         }
 444         logger.fine(() -> "Add method: " + fn.getSignature());
 445         int flags = ACC_PUBLIC | ACC_ABSTRACT;
 446         if (fn.isVarArgs) {
 447             flags |= ACC_VARARGS;
 448         }
 449         MethodVisitor mv = global_cw.visitMethod(flags,
 450                 funcTree.name(), fn.getDescriptor(), fn.getSignature(), null);
 451         final int arg_cnt = funcTree.numParams();
 452         for (int i = 0; i < arg_cnt; i++) {
 453             String name = funcTree.paramName(i);
 454             final int tmp = i;
 455             logger.finer(() -> "  arg " + tmp + ": " + name);
 456             mv.visitParameter(name, 0);
 457         }
 458         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 459         SourceLocation src = funcTree.location();
 460         SourceLocation.Location loc = src.getFileLocation();
 461         Path p = loc.path();
 462         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 463         av.visit("line", loc.line());
 464         av.visit("column", loc.column());
 465         av.visit("USR", funcTree.USR());
 466         av.visitEnd();
 467         Type type = funcTree.type();
 468         final String descStr = Utils.getFunction(type).toString();
 469         addHeaderDecl(funcTree.name(), descStr);
 470 
 471         int idx = 0;
 472         for (JType arg: fn.args) {
 473             if (arg instanceof TypeAlias) {
 474                 TypeAlias alias = (TypeAlias) arg;
 475                 final int tmp = idx;
 476                 logger.finest(() -> "  arg " + tmp + " is an alias " + alias);
 477                 if (alias.getAnnotationDescriptor() != null) {
 478                     mv.visitTypeAnnotation(
 479                             TypeReference.newFormalParameterReference(idx).getValue(),
 480                             null, alias.getAnnotationDescriptor(), true)
 481                       .visitEnd();
 482                 }
 483             }
 484             idx++;
 485         }
 486 
 487         if (fn.returnType instanceof TypeAlias) {
 488             TypeAlias alias = (TypeAlias) fn.returnType;
 489             logger.finest(() -> "  return type is an alias " + alias);
 490             if (alias.getAnnotationDescriptor() != null) {
 491                 mv.visitTypeAnnotation(
 492                         TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue(),
 493                         null, alias.getAnnotationDescriptor(), true)
 494                   .visitEnd();
 495             }
 496         }
 497         mv.visitEnd();
 498         if (staticForwardGen != null) {
 499             staticForwardGen.visitFunction(funcTree, jt);
 500         }
 501         return null;
 502     }
 503 
 504     protected AsmCodeFactory addType(JType jt, Tree tree) {
 505         JType2 jt2 = null;
 506         if (jt instanceof JType2) {
 507             jt2 = (JType2) jt;
 508             jt = jt2.getDelegate();
 509         } else {
 510             logger.warning(() -> "Should have JType2 in addType");
 511             if (Main.DEBUG) {
 512                 new Throwable().printStackTrace(ctx.err);
 513             }
 514         }
 515         if (tree == null) {
 516             assert (jt2 != null);
 517             if (jt instanceof JType.FnIf) {
 518                 createFunctionalInterface(null, (JType.FnIf) jt);
 519             }
 520             return this;
 521         }
 522         /*
 523         // FIXME: what is this?
 524         boolean noDef = cursor.isInvalid();
 525         if (noDef) {
 526             cursor = jt2.getCursor();
 527         }
 528         */
 529 
 530         try {
 531             logger.fine(() -> "Process tree " + tree.name());
 532             tree.accept(this, jt);
 533         } catch (Exception ex) {
 534             handleException(ex);
 535             logger.warning("Tree causing above exception is: " + tree.name());
 536             logger.warning(() -> tree.toString());
 537         }
 538         return this;
 539     }
 540 
 541     @Override
 542     public Void visitMacro(MacroTree macroTree, JType jt) {
 543         if (!macroTree.isConstant()) {
 544             logger.fine(() -> "Skipping unrecognized object-like macro " + macroTree.name());
 545             return null;
 546         }
 547         String name = macroTree.name();
 548         Object value = macroTree.value().get();
 549         if (! global_macros.add(name)) {
 550             return null; // added already
 551         }
 552         logger.fine(() -> "Adding macro " + name);
 553         Class<?> macroType = Utils.unboxIfNeeded(value.getClass());
 554 
 555         String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(macroType));
 556         MethodVisitor mv = global_cw.visitMethod(ACC_PUBLIC, name, sig, sig, null);
 557 
 558         AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
 559         SourceLocation src = macroTree.location();
 560         SourceLocation.Location loc = src.getFileLocation();
 561         Path p = loc.path();
 562         av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
 563         av.visit("line", loc.line());
 564         av.visit("column", loc.column());
 565         av.visit("USR", macroTree.USR());
 566         av.visitEnd();
 567 
 568         mv.visitCode();
 569 
 570         mv.visitLdcInsn(value);
 571         if (macroType.equals(char.class)) {
 572             mv.visitInsn(I2C);
 573             mv.visitInsn(IRETURN);
 574         } else if (macroType.equals(int.class)) {
 575             mv.visitInsn(IRETURN);
 576         } else if (macroType.equals(float.class)) {
 577             mv.visitInsn(FRETURN);
 578         } else if (macroType.equals(long.class)) {
 579             mv.visitInsn(LRETURN);
 580         } else if (macroType.equals(double.class)) {
 581             mv.visitInsn(DRETURN);
 582         } else if (macroType.equals(String.class)) {
 583             mv.visitInsn(ARETURN);
 584         }
 585         mv.visitMaxs(0, 0);
 586         mv.visitEnd();
 587         if (staticForwardGen != null) {
 588             staticForwardGen.visitMacro(macroTree, jt);
 589         }
 590         return null;
 591     }
 592 
 593     protected synchronized void produce() {
 594         if (built) {
 595             throw new IllegalStateException("Produce is called multiple times");
 596         }
 597         built = true;
 598         generateNativeHeader();
 599         try {
 600             writeClassFile(global_cw, owner.clsName);
 601         } catch (IOException ex) {
 602             handleException(ex);
 603         }
 604         if (staticForwardGen != null) {
 605             types.put(staticForwardGen.getSimpleClassName(), staticForwardGen.getClassBytes());
 606         }
 607     }
 608 
 609     protected Map<String, byte[]> collect() {
 610         // Ensure classes are produced
 611         if (!built) produce();
 612         HashMap<String, byte[]> rv = new HashMap<>();
 613         // Not copying byte[] for efficiency, perhaps not a safe idea though
 614         if (owner.pkgName.isEmpty()) {
 615             types.forEach((clsName, bytecodes) -> {
 616                 rv.put(clsName, bytecodes);
 617             });
 618         } else {
 619             types.forEach((clsName, bytecodes) -> {
 620                 rv.put(owner.pkgName + "." + clsName, bytecodes);
 621             });
 622         }
 623         return Collections.unmodifiableMap(rv);
 624     }
 625 }