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 jdk.internal.clang.*; 26 import jdk.internal.clang.Type; 27 import jdk.internal.org.objectweb.asm.*; 28 29 import java.io.IOException; 30 import java.nio.file.Path; 31 import java.nio.file.Paths; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.Map; 36 import java.util.logging.Logger; 37 import java.util.stream.Stream; 38 39 import static jdk.internal.org.objectweb.asm.Opcodes.*; 40 41 /** 42 * Scan a header file and generate classes for entities defined in that header 43 * file. 44 */ 45 final class AsmCodeFactory extends CodeFactory { 46 private static final String ANNOTATION_PKG_PREFIX = "Ljava/nicl/metadata/"; 47 private static final String ARRAY = ANNOTATION_PKG_PREFIX + "Array;"; 48 private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;"; 49 private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;"; 50 private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;"; 51 private static final String NATIVE_TYPE = ANNOTATION_PKG_PREFIX + "NativeType;"; 52 private static final String OFFSET = ANNOTATION_PKG_PREFIX + "Offset;"; 53 54 private final Context ctx; 55 private final ClassWriter global_cw; 56 private final String internal_name; 57 private final HeaderFile owner; 58 private final Map<String, byte[]> types; 59 private final HashSet<String> handledMacros = new HashSet<>(); 60 private final Logger logger = Logger.getLogger(getClass().getPackage().getName()); 61 private final StringBuilder headerDeclarations = new StringBuilder(); 62 63 AsmCodeFactory(Context ctx, HeaderFile header) { 64 this.ctx = ctx; 65 logger.info(() -> "Instantiate AsmCodeFactory for " + header.path); 66 this.owner = header; 67 this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName); 68 this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 69 this.types = new HashMap<>(); 70 global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 71 internal_name, 72 null, "java/lang/Object", null); 73 } 74 75 private void generateNativeHeader() { 76 AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true); 77 av.visit("path", owner.path.toAbsolutePath().toString()); 78 if (owner.libraries != null && !owner.libraries.isEmpty()) { 79 AnnotationVisitor libNames = av.visitArray("libraries"); 80 for (String name : owner.libraries) { 81 libNames.visit(null, name); 82 } 83 libNames.visitEnd(); 84 if (owner.libraryPaths != null && !owner.libraryPaths.isEmpty()) { 85 AnnotationVisitor libPaths = av.visitArray("libraryPaths"); 86 for (String path : owner.libraryPaths) { 87 libPaths.visit(null, path); 88 } 89 libPaths.visitEnd(); 90 } 91 } 92 av.visit("declarations", headerDeclarations.toString()); 93 av.visitEnd(); 94 } 95 96 private void handleException(Exception ex) { 97 ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex)); 98 if (Main.DEBUG) { 99 ex.printStackTrace(ctx.err); 100 } 101 } 102 103 private void annotateNativeLocation(ClassVisitor cw, Cursor dcl) { 104 AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true); 105 SourceLocation src = dcl.getSourceLocation(); 106 SourceLocation.Location loc = src.getFileLocation(); 107 Path p = loc.path(); 108 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 109 av.visit("line", loc.line()); 110 av.visit("column", loc.column()); 111 av.visit("USR", dcl.USR()); 112 av.visitEnd(); 113 } 114 115 private void writeClassFile(final ClassWriter cw, String clsName) 116 throws IOException { 117 cw.visitEnd(); 118 byte[] bytecodes = cw.toByteArray(); 119 if (null != types.put(clsName, bytecodes)) { 120 logger.warning("Class " + clsName + " definition is overwritten"); 121 } 122 } 123 124 /** 125 * 126 * @param cw ClassWriter for the struct 127 * @param c The FieldDecl cursor 128 * @param parentType The struct type 129 */ 130 private void addField(ClassVisitor cw, Cursor c, Type parentType) { 131 String fieldName = c.spelling(); 132 Type t = c.type(); 133 JType jt = owner.globalLookup(t); 134 assert (jt != null); 135 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get", 136 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null); 137 138 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); 139 SourceLocation src = c.getSourceLocation(); 140 SourceLocation.Location loc = src.getFileLocation(); 141 Path p = loc.path(); 142 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 143 av.visit("line", loc.line()); 144 av.visit("column", loc.column()); 145 av.visit("USR", c.USR()); 146 av.visitEnd(); 147 148 av = mv.visitAnnotation(NATIVE_TYPE, true); 149 av.visit("layout", Utils.getLayout(t)); 150 av.visit("ctype", t.spelling()); 151 av.visit("name", fieldName); 152 av.visitEnd(); 153 154 if (t.kind() == TypeKind.ConstantArray || t.kind() == TypeKind.IncompleteArray) { 155 logger.finer(() -> "Array field " + fieldName + ", type " + t.kind().name()); 156 av = mv.visitAnnotation(ARRAY, true); 157 av.visit("elementType", t.getElementType().canonicalType().spelling()); 158 av.visit("elementSize", t.getElementType().canonicalType().size()); 159 if (t.kind() == TypeKind.ConstantArray) { 160 av.visit("length", t.getNumberOfElements()); 161 } 162 av.visitEnd(); 163 } 164 165 if (parentType != null) { 166 long offset = parentType.getOffsetOf(fieldName); 167 av = mv.visitAnnotation(OFFSET, true); 168 av.visit("offset", offset); 169 if (c.isBitField()) { 170 av.visit("bits", c.getBitFieldWidth()); 171 } 172 av.visitEnd(); 173 } 174 mv.visitEnd(); 175 cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set", 176 "(" + jt.getDescriptor() + ")V", "(" + jt.getSignature() + ")V", null); 177 JType ptrType = new PointerType(jt); 178 cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr", 179 "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(), null); 180 } 181 182 private void addConstant(ClassVisitor cw, Cursor c) { 183 assert (c.kind() == CursorKind.EnumConstantDecl); 184 String name = c.spelling(); 185 String desc = owner.globalLookup(c.type()).getDescriptor(); 186 Object value = null; 187 switch (desc) { 188 case "J": 189 value = c.getEnumConstantValue(); 190 break; 191 case "I": 192 value = (int) c.getEnumConstantValue(); 193 break; 194 } 195 cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, name, desc, null, value); 196 } 197 198 private void createStruct(Cursor cursor) { 199 String nativeName = Utils.getIdentifier(cursor); 200 Type t = cursor.type(); 201 logger.fine(() -> "Create struct: " + nativeName); 202 203 String intf = Utils.toClassName(nativeName); 204 String name = internal_name + "$" + intf; 205 206 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 207 /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not 208 * taking static as a valid flag, so comment this out during development. 209 */ 210 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 211 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 212 cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT, 213 name, "Ljava/lang/Object;Ljava/nicl/types/Struct<L" + name + ";>;", 214 "java/lang/Object", new String[] {"java/nicl/types/Struct"}); 215 annotateNativeLocation(cw, cursor); 216 AnnotationVisitor av = cw.visitAnnotation(NATIVE_TYPE, true); 217 av.visit("layout", Utils.getLayout(t)); 218 av.visit("ctype", t.spelling()); 219 av.visit("size", t.size()); 220 av.visitEnd(); 221 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 222 223 // fields 224 Printer dbg = new Printer(); 225 structFields(cursor).forEachOrdered(cx -> addField(cw, cx, cursor.type())); 226 // Write class 227 try { 228 writeClassFile(cw, owner.clsName + "$" + intf); 229 } catch (IOException ex) { 230 handleException(ex); 231 } 232 } 233 234 // A stream of fields of a struct (or union). Note that we have to include 235 // fields from nested annoymous unions and structs in the containing struct. 236 private Stream<Cursor> structFields(Cursor cursor) { 237 return cursor.children() 238 .flatMap(c -> c.isAnonymousStruct()? structFields(c) : Stream.of(c)) 239 .filter(c -> c.kind() == CursorKind.FieldDecl); 240 } 241 242 private void createEnum(Cursor cursor) { 243 // define enum constants in global_cw 244 cursor.stream() 245 .filter(cx -> cx.kind() == CursorKind.EnumConstantDecl) 246 .forEachOrdered(cx -> addConstant(global_cw, cx)); 247 248 if (cursor.isAnonymousEnum()) { 249 // We are done with anonymous enum 250 return; 251 } 252 253 // generate annotation class for named enum 254 createAnnotationCls(cursor); 255 } 256 257 private void createAnnotationCls(Cursor dcl) { 258 String nativeName = Utils.getIdentifier(dcl); 259 logger.fine(() -> "Create annotation for: " + nativeName); 260 261 String intf = Utils.toClassName(nativeName); 262 String name = internal_name + "$" + intf; 263 264 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 265 global_cw.visitInnerClass(name, internal_name, intf, 266 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 267 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 268 String[] superAnno = { "java/lang/annotation/Annotation" }; 269 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION, 270 name, null, "java/lang/Object", superAnno); 271 annotateNativeLocation(cw, dcl); 272 Type t = dcl.type().canonicalType(); 273 AnnotationVisitor av = cw.visitAnnotation(NATIVE_TYPE, true); 274 av.visit("layout", Utils.getLayout(t)); 275 av.visit("ctype", t.spelling()); 276 av.visit("size", t.size()); 277 av.visitEnd(); 278 279 av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true); 280 av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE"); 281 av.visitEnd(); 282 av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); 283 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); 284 av.visitEnd(); 285 cw.visitInnerClass(name, internal_name, intf, 286 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 287 // Write class 288 try { 289 writeClassFile(cw, owner.clsName + "$" + intf); 290 } catch (IOException ex) { 291 handleException(ex); 292 } 293 } 294 295 private void createFunctionalInterface(JType2 jt2) { 296 JType.FnIf fnif = (JType.FnIf) jt2.getDelegate(); 297 JType.Function fn = fnif.getFunction(); 298 String intf = ((JType.InnerType) fnif.type).getName(); 299 logger.fine(() -> "Create FunctionalInterface " + intf); 300 String nDesc = jt2.getNativeDescriptor(); 301 302 final String name = internal_name + "$" + intf; 303 304 logger.fine(() -> "Define class " + name + " for anonymous function " + nDesc); 305 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 306 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 307 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 308 name, null, "java/lang/Object", null); 309 AnnotationVisitor av = cw.visitAnnotation( 310 "Ljava/lang/FunctionalInterface;", true); 311 av.visitEnd(); 312 av = cw.visitAnnotation(NATIVE_CALLBACK, true); 313 av.visit("value", nDesc); 314 av.visitEnd(); 315 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 316 317 // add the method 318 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn", 319 fn.getDescriptor(), fn.getSignature(), null); 320 av.visitEnd(); 321 322 mv.visitEnd(); 323 // Write class 324 try { 325 writeClassFile(cw, owner.clsName + "$" + intf); 326 } catch (IOException ex) { 327 handleException(ex); 328 } 329 } 330 331 private void createFunctionalInterface(Cursor dcl, JType.FnIf fnif) { 332 JType.Function fn = fnif.getFunction(); 333 String intf; 334 String nativeName; 335 if (dcl == null) { 336 intf = ((JType.InnerType) fnif.type).getName(); 337 nativeName = "N/A"; 338 } else { 339 nativeName = Utils.getIdentifier(dcl); 340 intf = Utils.toClassName(nativeName); 341 } 342 logger.fine(() -> "Create FunctionalInterface " + intf); 343 344 final String name = internal_name + "$" + intf; 345 346 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 347 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 348 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 349 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 350 name, null, "java/lang/Object", null); 351 if (dcl != null) { 352 annotateNativeLocation(cw, dcl); 353 } 354 AnnotationVisitor av = cw.visitAnnotation( 355 "Ljava/lang/FunctionalInterface;", true); 356 av.visitEnd(); 357 if (dcl != null) { 358 av = cw.visitAnnotation(NATIVE_CALLBACK, true); 359 Type t = dcl.type().canonicalType(); 360 av.visit("value", Utils.getLayout(t)); 361 av.visitEnd(); 362 } 363 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 364 365 // add the method 366 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn", 367 fn.getDescriptor(), fn.getSignature(), null); 368 mv.visitEnd(); 369 // Write class 370 try { 371 writeClassFile(cw, owner.clsName + "$" + intf); 372 } catch (IOException ex) { 373 handleException(ex); 374 } 375 } 376 377 private void defineType(Cursor dcl, TypeAlias alias) { 378 if (alias.getAnnotationDescriptor() != null) { 379 createAnnotationCls(dcl); 380 } else { 381 JType real = alias.canonicalType(); 382 if (real instanceof JType.FnIf) { 383 createFunctionalInterface(dcl, (JType.FnIf) real); 384 } 385 // Otherwise, type alias is a same named stuct 386 } 387 } 388 389 private void addMethod(Cursor dcl, JType.Function fn) { 390 logger.fine(() -> "Add method: " + fn.getSignature()); 391 int flags = ACC_PUBLIC | ACC_ABSTRACT; 392 if (fn.isVarArgs) { 393 flags |= ACC_VARARGS; 394 } 395 MethodVisitor mv = global_cw.visitMethod(flags, 396 dcl.spelling(), fn.getDescriptor(), fn.getSignature(), null); 397 final int arg_cnt = dcl.numberOfArgs(); 398 for (int i = 0; i < arg_cnt; i++) { 399 String name = dcl.getArgument(i).spelling(); 400 final int tmp = i; 401 logger.finer(() -> " arg " + tmp + ": " + name); 402 mv.visitParameter(name, 0); 403 } 404 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); 405 SourceLocation src = dcl.getSourceLocation(); 406 SourceLocation.Location loc = src.getFileLocation(); 407 Path p = loc.path(); 408 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 409 av.visit("line", loc.line()); 410 av.visit("column", loc.column()); 411 av.visit("USR", dcl.USR()); 412 av.visitEnd(); 413 av = mv.visitAnnotation(NATIVE_TYPE, true); 414 Type t = dcl.type(); 415 String layout = Utils.getLayout(t); 416 av.visit("layout", layout); 417 av.visit("ctype", t.spelling()); 418 av.visit("name", dcl.spelling()); 419 av.visitEnd(); 420 421 headerDeclarations.append(dcl.spelling()); 422 headerDeclarations.append('='); 423 headerDeclarations.append(layout); 424 headerDeclarations.append(' '); 425 426 int idx = 0; 427 for (JType arg: fn.args) { 428 if (arg instanceof TypeAlias) { 429 TypeAlias alias = (TypeAlias) arg; 430 final int tmp = idx; 431 logger.finest(() -> " arg " + tmp + " is an alias " + alias); 432 if (alias.getAnnotationDescriptor() != null) { 433 mv.visitTypeAnnotation( 434 TypeReference.newFormalParameterReference(idx).getValue(), 435 null, alias.getAnnotationDescriptor(), true) 436 .visitEnd(); 437 } 438 } 439 idx++; 440 } 441 442 if (fn.returnType instanceof TypeAlias) { 443 TypeAlias alias = (TypeAlias) fn.returnType; 444 logger.finest(() -> " return type is an alias " + alias); 445 if (alias.getAnnotationDescriptor() != null) { 446 mv.visitTypeAnnotation( 447 TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue(), 448 null, alias.getAnnotationDescriptor(), true) 449 .visitEnd(); 450 } 451 } 452 mv.visitEnd(); 453 } 454 455 @Override 456 protected CodeFactory addType(JType jt, Cursor cursor) { 457 JType2 jt2 = null; 458 if (jt instanceof JType2) { 459 jt2 = (JType2) jt; 460 jt = jt2.getDelegate(); 461 } else { 462 logger.warning(() -> "Should have JType2 in addType"); 463 if (Main.DEBUG) { 464 new Throwable().printStackTrace(ctx.err); 465 } 466 } 467 if (cursor == null) { 468 assert (jt2 != null); 469 if (jt instanceof JType.FnIf) { 470 createFunctionalInterface(jt2); 471 } 472 return this; 473 } 474 475 try { 476 logger.fine(() -> "Process cursor " + cursor.spelling()); 477 switch (cursor.kind()) { 478 case StructDecl: 479 case UnionDecl: 480 createStruct(cursor); 481 break; 482 case FunctionDecl: 483 assert (jt instanceof JType.Function); 484 addMethod(cursor, (JType.Function) jt); 485 break; 486 case EnumDecl: 487 createEnum(cursor); 488 break; 489 case TypedefDecl: 490 // anonymous typedef struct {} xxx will not get TypeAlias 491 if (jt instanceof TypeAlias) { 492 defineType(cursor, (TypeAlias) jt); 493 } 494 break; 495 case VarDecl: 496 addField(global_cw, cursor, null); 497 break; 498 default: 499 logger.warning(() -> "Unsupported declaration Cursor:"); 500 logger.warning(() -> Printer.Stringifier(p -> p.dumpCursor(cursor, true))); 501 break; 502 } 503 } catch (Exception ex) { 504 handleException(ex); 505 logger.warning("Cursor causing above exception is: " + cursor.spelling()); 506 logger.warning(() -> Printer.Stringifier(p -> p.dumpCursor(cursor, true))); 507 } 508 return this; 509 } 510 511 private static class Literal { 512 private enum Type { 513 INT(int.class), 514 LONG(long.class), 515 STRING(String.class); 516 517 private Class<?> c; 518 519 Type(Class<?> c) { 520 this.c = c; 521 } 522 523 Class<?> getTypeClass() { 524 return c; 525 } 526 } 527 528 private final Type type; 529 private final Object value; 530 531 Literal(Type type, Object value) { 532 this.type = type; 533 this.value = value; 534 } 535 536 Type type() { 537 return type; 538 } 539 540 Object getValue() { 541 return value; 542 } 543 544 private static Literal parseString(String s) { 545 if (!s.startsWith("\"") || !s.endsWith("\"")) { 546 return null; 547 } 548 549 return new Literal(Literal.Type.STRING, s.substring(1, s.length() - 1)); 550 } 551 552 private static Literal parseInteger(String s) { 553 try { 554 return new Literal(Literal.Type.INT, Integer.valueOf(s)); 555 } catch (NumberFormatException e) { 556 } 557 558 if (s.startsWith("0x")) { 559 try { 560 return new Literal(Literal.Type.INT, Integer.parseInt(s.substring(2), 16)); 561 } catch (NumberFormatException e) { 562 } 563 } 564 565 return null; 566 } 567 568 private static Literal parseLong(String s) { 569 try { 570 return new Literal(Literal.Type.LONG, Long.valueOf(s)); 571 } catch (NumberFormatException e) { 572 } 573 574 if (s.startsWith("0x")) { 575 try { 576 return new Literal(Literal.Type.LONG, Long.parseLong(s.substring(2), 16)); 577 } catch (NumberFormatException e) { 578 } 579 } 580 581 String[] ignoredSuffixes = {"L", "UL", "LL"}; 582 583 for (String suffix : ignoredSuffixes) { 584 if (s.endsWith(suffix)) { 585 Literal l = parseLong(s.substring(0, s.length() - suffix.length())); 586 if (l != null) { 587 return l; 588 } 589 } 590 } 591 592 return null; 593 } 594 595 static Literal parse(String s) { 596 Literal l; 597 598 l = parseString(s); 599 if (l != null) { 600 return l; 601 } 602 603 l = parseInteger(s); 604 if (l != null) { 605 return l; 606 } 607 608 l = parseLong(s); 609 if (l != null) { 610 return l; 611 } 612 613 return null; 614 } 615 } 616 617 @Override 618 protected CodeFactory addMacro(Cursor cursor) { 619 String macroName = cursor.spelling(); 620 621 if (handledMacros.contains(macroName)) { 622 logger.fine(() -> "Macro " + macroName + " already handled"); 623 return this; 624 } 625 626 logger.fine(() -> "Adding macro " + macroName); 627 628 TranslationUnit tu = cursor.getTranslationUnit(); 629 SourceRange range = cursor.getExtent(); 630 String[] tokens = tu.tokens(range); 631 632 for (String token : tokens) { 633 logger.finest(() -> "TOKEN: " + token); 634 } 635 636 if (tokens.length != 2) { 637 logger.fine(() -> "Skipping macro " + tokens[0] + " because it doesn't have exactly 2 tokens"); 638 return this; 639 } 640 641 int flags = ACC_PUBLIC; 642 643 Literal l = Literal.parse(tokens[1]); 644 if (l == null) { 645 logger.fine(() -> "Skipping macro " + tokens[0] + " because its body isn't a recognizable literal constant"); 646 return this; 647 } 648 649 String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(l.type().getTypeClass())); 650 MethodVisitor mv = global_cw.visitMethod(flags, macroName, sig, sig, null); 651 652 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); 653 SourceLocation src = cursor.getSourceLocation(); 654 SourceLocation.Location loc = src.getFileLocation(); 655 Path p = loc.path(); 656 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 657 av.visit("line", loc.line()); 658 av.visit("column", loc.column()); 659 av.visit("USR", cursor.USR()); 660 av.visitEnd(); 661 662 mv.visitCode(); 663 mv.visitLdcInsn(l.getValue()); 664 switch (l.type()) { 665 case INT: 666 mv.visitInsn(IRETURN); 667 break; 668 case LONG: 669 mv.visitInsn(LRETURN); 670 break; 671 case STRING: 672 mv.visitInsn(ARETURN); 673 break; 674 } 675 mv.visitMaxs(0, 0); 676 mv.visitEnd(); 677 678 handledMacros.add(macroName); 679 680 return this; 681 } 682 683 @Override 684 protected void produce() { 685 generateNativeHeader(); 686 try { 687 writeClassFile(global_cw, owner.clsName); 688 } catch (IOException ex) { 689 handleException(ex); 690 } 691 } 692 693 @Override 694 protected Map<String, byte[]> collect() { 695 // Ensure classes are produced 696 produce(); 697 HashMap<String, byte[]> rv = new HashMap<>(); 698 // Not copying byte[] for efficiency, perhaps not a safe idea though 699 if (owner.pkgName.isEmpty()) { 700 types.forEach((clsName, bytecodes) -> { 701 rv.put(clsName, bytecodes); 702 }); 703 } else { 704 types.forEach((clsName, bytecodes) -> { 705 rv.put(owner.pkgName + "." + clsName, bytecodes); 706 }); 707 } 708 return Collections.unmodifiableMap(rv); 709 } 710 711 public static void main(String[] args) throws IOException { 712 final Path file = Paths.get(args[1]); 713 final String pkg = args[0]; 714 Context ctx = new Context(); 715 ctx.usePackageForFolder(file, pkg); 716 ctx.usePackageForFolder(Paths.get("/usr/include"), "system"); 717 ctx.addSource(file); 718 ctx.parse(); 719 ctx.collectJarFile(Paths.get(args[2]), pkg); 720 } 721 }