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