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