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