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