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 38 import static jdk.internal.org.objectweb.asm.Opcodes.*; 39 40 /** 41 * Scan a header file and generate classes for entities defined in that header 42 * file. 43 */ 44 public class AsmCodeFactory extends CodeFactory { 45 final ClassWriter global_cw; 46 final String internal_name; 47 final HeaderFile owner; 48 final Map<String, byte[]> types; 49 final HashSet<String> handledMacros = new HashSet<>(); 50 final Logger logger = Logger.getLogger(getClass().getPackage().getName()); 51 52 public AsmCodeFactory(HeaderFile header) { 53 logger.info(() -> "Instantiate AsmCodeFactory for " + header.path); 54 owner = header; 55 internal_name = Utils.toInternalName(owner.pkgName, owner.clsName); 56 global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 57 types = new HashMap<>(); 58 init(); 59 } 60 61 private void init() { 62 global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 63 internal_name, 64 null, "java/lang/Object", null); 65 AnnotationVisitor av = global_cw.visitAnnotation( 66 "Ljava/nicl/metadata/Header;", true); 67 av.visit("path", owner.path.toAbsolutePath().toString()); 68 av.visitEnd(); 69 } 70 71 private void annotateC(ClassVisitor cw, Cursor dcl) { 72 AnnotationVisitor av = cw.visitAnnotation( 73 "Ljava/nicl/metadata/C;", true); 74 SourceLocation src = dcl.getSourceLocation(); 75 SourceLocation.Location loc = src.getFileLocation(); 76 Path p = loc.path(); 77 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 78 av.visit("line", loc.line()); 79 av.visit("column", loc.column()); 80 av.visit("USR", dcl.USR()); 81 av.visitEnd(); 82 } 83 84 private void writeClassFile(final ClassWriter cw, String clsName) 85 throws IOException { 86 cw.visitEnd(); 87 byte[] bytecodes = cw.toByteArray(); 88 types.put(clsName, bytecodes); 89 } 90 91 /** 92 * 93 * @param cw ClassWriter for the struct 94 * @param c The FieldDecl cursor 95 * @param parentType The struct type 96 */ 97 private void addField(ClassVisitor cw, Cursor c, Type parentType) { 98 String fieldName = c.spelling(); 99 Type t = c.type(); 100 JType jt = owner.globalLookup(t); 101 assert (jt != null); 102 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get", 103 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null); 104 105 AnnotationVisitor av = mv.visitAnnotation( 106 "Ljava/nicl/metadata/C;", true); 107 SourceLocation src = c.getSourceLocation(); 108 SourceLocation.Location loc = src.getFileLocation(); 109 Path p = loc.path(); 110 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 111 av.visit("line", loc.line()); 112 av.visit("column", loc.column()); 113 av.visit("USR", c.USR()); 114 av.visitEnd(); 115 116 av = mv.visitAnnotation( 117 "Ljava/nicl/metadata/NativeType;", true); 118 av.visit("layout", Utils.getLayout(t)); 119 av.visit("ctype", t.spelling()); 120 av.visit("size", t.size()); 121 av.visit("name", fieldName); 122 av.visitEnd(); 123 124 if (t.kind() == TypeKind.ConstantArray || t.kind() == TypeKind.IncompleteArray) { 125 logger.finer(() -> "Array field " + fieldName + ", type " + t.kind().name()); 126 av = mv.visitAnnotation("Ljava/nicl/metadata/Array;", true); 127 av.visit("elementType", t.getElementType().canonicalType().spelling()); 128 av.visit("elementSize", t.getElementType().canonicalType().size()); 129 if (t.kind() == TypeKind.ConstantArray) { 130 av.visit("length", t.getNumberOfElements()); 131 } 132 av.visitEnd(); 133 } 134 135 if (parentType != null) { 136 long offset = parentType.getOffsetOf(fieldName); 137 av = mv.visitAnnotation( 138 "Ljava/nicl/metadata/Offset;", true); 139 av.visit("offset", offset); 140 if (c.isBitField()) { 141 av.visit("bits", c.getBitFieldWidth()); 142 } 143 av.visitEnd(); 144 } 145 mv.visitEnd(); 146 cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set", 147 "(" + jt.getDescriptor() + ")V", "(" + jt.getSignature() + ")V", null); 148 // Use long for a reference for now 149 JType refType = new ReferenceType(jt); 150 cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ref", 151 "()" + refType.getDescriptor(), "()" + refType.getSignature(), null); 152 } 153 154 private void addConstant(ClassVisitor cw, Cursor c) { 155 assert (c.kind() == CursorKind.EnumConstantDecl); 156 String name = c.spelling(); 157 String desc = owner.globalLookup(c.type()).getDescriptor(); 158 Object value = null; 159 switch (desc) { 160 case "J": 161 value = c.getEnumConstantValue(); 162 break; 163 case "I": 164 value = (int) c.getEnumConstantValue(); 165 break; 166 } 167 cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, name, desc, null, value); 168 } 169 170 private void createStruct(Cursor cursor) { 171 String nativeName = Utils.getIdentifier(cursor); 172 Type t = cursor.type(); 173 logger.fine(() -> "Create struct: " + nativeName); 174 175 String intf = Utils.toClassName(nativeName); 176 String name = internal_name + "$" + intf; 177 178 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 179 /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not 180 * taking static as a valid flag, so comment this out during development. 181 */ 182 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 183 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 184 cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT, 185 name, "Ljava/lang/Object;Ljava/nicl/types/Reference<L" + name + ";>;", 186 "java/lang/Object", new String[] {"java/nicl/types/Reference"}); 187 annotateC(cw, cursor); 188 AnnotationVisitor av = cw.visitAnnotation( 189 "Ljava/nicl/metadata/NativeType;", true); 190 av.visit("layout", Utils.getLayout(t)); 191 av.visit("ctype", t.spelling()); 192 av.visit("size", t.size()); 193 av.visit("isRecordType", true); 194 av.visitEnd(); 195 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 196 197 // fields 198 Printer dbg = new Printer(); 199 cursor.stream() 200 .filter(cx -> cx.kind() == CursorKind.FieldDecl) 201 .forEachOrdered(cx -> addField(cw, cx, cursor.type())); 202 // Write class 203 try { 204 writeClassFile(cw, owner.clsName + "$" + intf); 205 } catch (IOException ex) { 206 ex.printStackTrace(System.err); 207 } 208 } 209 210 private void createEnum(Cursor cursor) { 211 String nativeName = Utils.getIdentifier(cursor); 212 logger.fine(() -> "create enum: " + nativeName); 213 214 String intf = Utils.toClassName(nativeName); 215 String name = internal_name + "$" + intf; 216 217 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 218 global_cw.visitInnerClass(name, internal_name, intf, 219 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 220 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 221 String[] superAnno = { "java/lang/annotation/Annotation" }; 222 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION, 223 name, null, "java/lang/Object", superAnno); 224 annotateC(cw, cursor); 225 AnnotationVisitor av = cw.visitAnnotation( 226 "Ljava/nicl/metadata/NativeType;", true); 227 Type t = cursor.getEnumDeclIntegerType(); 228 av.visit("layout", Utils.getLayout(t)); 229 av.visit("ctype", t.spelling()); 230 av.visit("size", t.size()); 231 av.visitEnd(); 232 233 av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true); 234 av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE"); 235 av.visitEnd(); 236 av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); 237 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); 238 av.visitEnd(); 239 cw.visitInnerClass(name, internal_name, intf, 240 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 241 // constants 242 cursor.stream() 243 .filter(cx -> cx.kind() == CursorKind.EnumConstantDecl) 244 .forEachOrdered(cx -> addConstant(cw, cx)); 245 // Write class 246 try { 247 writeClassFile(cw, owner.clsName + "$" + intf); 248 } catch (IOException ex) { 249 ex.printStackTrace(System.err); 250 } 251 } 252 253 private void createAnnotationCls(Cursor dcl) { 254 String nativeName = Utils.getIdentifier(dcl); 255 logger.fine(() -> "Create annotation for: " + nativeName); 256 257 String intf = Utils.toClassName(nativeName); 258 String name = internal_name + "$" + intf; 259 260 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 261 global_cw.visitInnerClass(name, internal_name, intf, 262 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 263 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 264 String[] superAnno = { "java/lang/annotation/Annotation" }; 265 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION, 266 name, null, "java/lang/Object", superAnno); 267 annotateC(cw, dcl); 268 Type t = dcl.type().canonicalType(); 269 AnnotationVisitor av = cw.visitAnnotation( 270 "Ljava/nicl/metadata/NativeType;", true); 271 av.visit("layout", Utils.getLayout(t)); 272 av.visit("ctype", t.spelling()); 273 av.visit("size", t.size()); 274 av.visitEnd(); 275 276 av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true); 277 av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE"); 278 av.visitEnd(); 279 av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); 280 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); 281 av.visitEnd(); 282 cw.visitInnerClass(name, internal_name, intf, 283 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 284 // Write class 285 try { 286 writeClassFile(cw, owner.clsName + "$" + intf); 287 } catch (IOException ex) { 288 ex.printStackTrace(System.err); 289 } 290 } 291 292 private void createFunctionalInterface(JType2 jt2) { 293 JType.FnIf fnif = (JType.FnIf) jt2.getDelegate(); 294 JType.Function fn = fnif.getFunction(); 295 String intf = ((JType.InnerType) fnif.type).getName(); 296 logger.fine(() -> "Create FunctionalInterface " + intf); 297 String nDesc = jt2.getNativeDescriptor(); 298 299 final String name = internal_name + "$" + intf; 300 301 logger.fine(() -> "Define class " + name + " for anonymous function " + nDesc); 302 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 303 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 304 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 305 name, null, "java/lang/Object", null); 306 AnnotationVisitor av = cw.visitAnnotation( 307 "Ljava/lang/FunctionalInterface;", true); 308 av.visitEnd(); 309 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 310 311 // add the method 312 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn", 313 fn.getDescriptor(), fn.getSignature(), null); 314 av = mv.visitAnnotation( 315 "Ljava/nicl/metadata/NativeType;", true); 316 av.visit("layout", nDesc); 317 av.visit("ctype", "N/A"); 318 av.visit("size", -1); 319 av.visitEnd(); 320 // FIXME: We need calling convention 321 av = mv.visitAnnotation( 322 "Ljava/nicl/metadata/CallingConvention;", true); 323 av.visit("value", jt2.getCallingConvention()); 324 av.visitEnd(); 325 326 mv.visitEnd(); 327 // Write class 328 try { 329 writeClassFile(cw, owner.clsName + "$" + intf); 330 } catch (IOException ex) { 331 ex.printStackTrace(System.err); 332 } 333 } 334 335 private void createFunctionalInterface(Cursor dcl, JType.FnIf fnif) { 336 JType.Function fn = fnif.getFunction(); 337 String intf; 338 String nativeName; 339 if (dcl == null) { 340 intf = ((JType.InnerType) fnif.type).getName(); 341 nativeName = "N/A"; 342 } else { 343 nativeName = Utils.getIdentifier(dcl); 344 intf = Utils.toClassName(nativeName); 345 } 346 logger.fine(() -> "Create FunctionalInterface " + intf); 347 348 final String name = internal_name + "$" + intf; 349 350 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 351 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 352 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 353 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 354 name, null, "java/lang/Object", null); 355 if (dcl != null) { 356 annotateC(cw, dcl); 357 } 358 AnnotationVisitor av = cw.visitAnnotation( 359 "Ljava/lang/FunctionalInterface;", true); 360 av.visitEnd(); 361 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 362 363 // add the method 364 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn", 365 fn.getDescriptor(), fn.getSignature(), null); 366 if (dcl != null) { 367 av = mv.visitAnnotation( 368 "Ljava/nicl/metadata/NativeType;", true); 369 Type t = dcl.type().canonicalType(); 370 av.visit("layout", Utils.getLayout(t)); 371 av.visit("ctype", t.spelling()); 372 av.visit("size", t.size()); 373 av.visitEnd(); 374 av = mv.visitAnnotation( 375 "Ljava/nicl/metadata/CallingConvention;", 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 ex.printStackTrace(System.err); 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( 416 "Ljava/nicl/metadata/C;", true); 417 SourceLocation src = dcl.getSourceLocation(); 418 SourceLocation.Location loc = src.getFileLocation(); 419 Path p = loc.path(); 420 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 421 av.visit("line", loc.line()); 422 av.visit("column", loc.column()); 423 av.visit("USR", dcl.USR()); 424 av.visitEnd(); 425 av = mv.visitAnnotation( 426 "Ljava/nicl/metadata/NativeType;", true); 427 Type t = dcl.type(); 428 av.visit("layout", Utils.getLayout(t)); 429 av.visit("ctype", t.spelling()); 430 av.visit("name", dcl.spelling()); 431 av.visit("size", t.size()); 432 av.visitEnd(); 433 av = mv.visitAnnotation( 434 "Ljava/nicl/metadata/CallingConvention;", true); 435 av.visit("value", t.getCallingConvention().value()); 436 av.visitEnd(); 437 438 int idx = 0; 439 for (JType arg: fn.args) { 440 if (arg instanceof TypeAlias) { 441 TypeAlias alias = (TypeAlias) arg; 442 final int tmp = idx; 443 logger.finest(() -> " arg " + tmp + " is an alias " + alias); 444 if (alias.getAnnotationDescriptor() != null) { 445 mv.visitTypeAnnotation( 446 TypeReference.newFormalParameterReference(idx).getValue(), 447 null, alias.getAnnotationDescriptor(), true) 448 .visitEnd(); 449 } 450 } 451 idx++; 452 } 453 454 if (fn.returnType instanceof TypeAlias) { 455 TypeAlias alias = (TypeAlias) fn.returnType; 456 logger.finest(() -> " return type is an alias " + alias); 457 if (alias.getAnnotationDescriptor() != null) { 458 mv.visitTypeAnnotation( 459 TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue(), 460 null, alias.getAnnotationDescriptor(), true) 461 .visitEnd(); 462 } 463 } 464 mv.visitEnd(); 465 } 466 467 @Override 468 protected CodeFactory addType(JType jt, Cursor cursor) { 469 JType2 jt2 = null; 470 if (jt instanceof JType2) { 471 jt2 = (JType2) jt; 472 jt = jt2.getDelegate(); 473 } else { 474 logger.warning(() -> "Should have JType2 in addType"); 475 new Throwable().printStackTrace(System.err); 476 } 477 if (cursor == null) { 478 assert (jt2 != null); 479 if (jt instanceof JType.FnIf) { 480 createFunctionalInterface(jt2); 481 } 482 return this; 483 } 484 485 try { 486 logger.fine(() -> "Process cursor " + cursor.spelling()); 487 switch (cursor.kind()) { 488 case StructDecl: 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.fine(() -> Printer.Stringifier(p -> p.dumpCursor(cursor, true))); 510 break; 511 } 512 } catch (Exception ex) { 513 ex.printStackTrace(System.err); 514 logger.warning("Cursor causing above exception is: " + cursor.spelling()); 515 logger.fine(() -> 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 long tu = cursor.getTranslationUnit(); 638 SourceRange range = cursor.getExtent(); 639 String[] tokens = Index.tokenize(tu, 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("Ljava/nicl/metadata/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 ex.printStackTrace(System.err); 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 = Context.getInstance(); 723 ctx.usePackageForFolder(file, pkg); 724 ctx.usePackageForFolder(Paths.get("/usr/include"), "system"); 725 ctx.sources.add(file); 726 ctx.parse(AsmCodeFactory::new); 727 ctx.collectJarFile(Paths.get(args[2]), pkg); 728 } 729 730 }