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