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