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