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