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