1 /* 2 * Copyright (c) 2007, 2025, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javap; 27 28 import java.lang.classfile.Annotation; 29 import java.lang.classfile.Attribute; 30 import java.lang.classfile.Attributes; 31 import java.lang.classfile.Signature; 32 import java.lang.classfile.TypeAnnotation; 33 import java.lang.classfile.attribute.*; 34 import java.lang.classfile.constantpool.ModuleEntry; 35 import java.lang.classfile.constantpool.NameAndTypeEntry; 36 import java.lang.classfile.constantpool.PoolEntry; 37 import java.lang.classfile.constantpool.Utf8Entry; 38 import java.lang.reflect.AccessFlag; 39 import java.lang.reflect.ClassFileFormatVersion; 40 import java.lang.reflect.Modifier; 41 import java.nio.charset.StandardCharsets; 42 import java.util.List; 43 import java.util.Locale; 44 45 import static java.lang.classfile.ClassFile.ACC_MANDATED; 46 import static java.lang.classfile.ClassFile.ACC_SYNTHETIC; 47 import static java.lang.classfile.attribute.StackMapFrameInfo.*; 48 import static java.lang.classfile.instruction.CharacterRange.*; 49 50 import com.sun.tools.javac.util.Assert; 51 import com.sun.tools.javac.util.StringUtils; 52 53 /* 54 * A writer for writing Attributes as text. 55 * 56 * <p><b>This is NOT part of any supported API. 57 * If you write code that depends on this, you do so at your own risk. 58 * This code and its internal interfaces are subject to change or 59 * deletion without notice.</b> 60 */ 61 public class AttributeWriter extends BasicWriter { 62 63 public static AttributeWriter instance(Context context) { 64 AttributeWriter instance = context.get(AttributeWriter.class); 65 if (instance == null) 66 instance = new AttributeWriter(context); 67 return instance; 68 } 69 70 protected AttributeWriter(Context context) { 71 super(context); 72 context.put(AttributeWriter.class, this); 73 classWriter = ClassWriter.instance(context); 74 annotationWriter = AnnotationWriter.instance(context); 75 codeWriter = CodeWriter.instance(context); 76 constantWriter = ConstantWriter.instance(context); 77 options = Options.instance(context); 78 } 79 80 public void write(List<Attribute<?>> attrs, ClassFileFormatVersion cffv) { 81 write(attrs, null, cffv); 82 } 83 84 public void write(List<Attribute<?>> attrs, CodeAttribute lr, ClassFileFormatVersion cffv) { 85 if (attrs != null) { 86 for (var attr : attrs) try { 87 write(attr, lr, cffv); 88 } catch (IllegalArgumentException e) { 89 report(e); 90 } 91 } 92 } 93 94 public void write(Attribute<?> a, CodeAttribute lr, ClassFileFormatVersion cffv) { 95 switch (a) { 96 case UnknownAttribute attr -> { 97 byte[] data = attr.contents(); 98 int i = 0; 99 int j = 0; 100 print(" "); 101 print(attr.attributeName().stringValue()); 102 print(": "); 103 print("length = 0x" + toHex(data.length)); 104 print(" (unknown attribute)"); 105 println(); 106 print(" "); 107 while (i < data.length) { 108 print(toHex(data[i], 2)); 109 110 j++; 111 if (j == 16) { 112 println(); 113 print(" "); 114 j = 0; 115 } else { 116 print(" "); 117 } 118 i++; 119 } 120 println(); 121 } 122 case AnnotationDefaultAttribute attr -> { 123 println("AnnotationDefault:"); 124 indent(+1); 125 print("default_value: "); 126 annotationWriter.write(attr.defaultValue()); 127 indent(-1); 128 println(); 129 } 130 case BootstrapMethodsAttribute attr -> { 131 println("BootstrapMethods:"); 132 for (int i = 0; i < attr.bootstrapMethodsSize() ; i++) { 133 var bsm = attr.bootstrapMethods().get(i); 134 indent(+1); 135 print(i + ": #" + bsm.bootstrapMethod().index() + " "); 136 println(constantWriter.stringValue(bsm.bootstrapMethod())); 137 indent(+1); 138 println("Method arguments:"); 139 indent(+1); 140 for (var arg : bsm.arguments()) { 141 print("#" + arg.index() + " "); 142 println(constantWriter.stringValue(arg)); 143 } 144 indent(-3); 145 } 146 } 147 case CharacterRangeTableAttribute attr -> { 148 println("CharacterRangeTable:"); 149 indent(+1); 150 for (var e : attr.characterRangeTable()) { 151 print(String.format(" %2d, %2d, %6x, %6x, %4x", 152 e.startPc(), e.endPc(), 153 e.characterRangeStart(), e.characterRangeEnd(), 154 e.flags())); 155 tab(); 156 print(String.format("// %2d, %2d, %4d:%02d, %4d:%02d", 157 e.startPc(), e.endPc(), 158 (e.characterRangeStart() >> 10), 159 (e.characterRangeStart() & 0x3ff), 160 (e.characterRangeEnd() >> 10), 161 (e.characterRangeEnd() & 0x3ff))); 162 if ((e.flags() & FLAG_STATEMENT) != 0) 163 print(", statement"); 164 if ((e.flags() & FLAG_BLOCK) != 0) 165 print(", block"); 166 if ((e.flags() & FLAG_ASSIGNMENT) != 0) 167 print(", assignment"); 168 if ((e.flags() & FLAG_FLOW_CONTROLLER) != 0) 169 print(", flow-controller"); 170 if ((e.flags() & FLAG_FLOW_TARGET) != 0) 171 print(", flow-target"); 172 if ((e.flags() & FLAG_INVOKE) != 0) 173 print(", invoke"); 174 if ((e.flags() & FLAG_CREATE) != 0) 175 print(", create"); 176 if ((e.flags() & FLAG_BRANCH_TRUE) != 0) 177 print(", branch-true"); 178 if ((e.flags() & FLAG_BRANCH_FALSE) != 0) 179 print(", branch-false"); 180 println(); 181 } 182 indent(-1); 183 } 184 case CodeAttribute attr -> codeWriter.write(attr); 185 case CompilationIDAttribute attr -> 186 constantWriter.write(attr.compilationId().index()); 187 case ConstantValueAttribute attr -> { 188 print("ConstantValue: "); 189 constantWriter.write(attr.constant().index()); 190 println(); 191 } 192 case DeprecatedAttribute attr -> println("Deprecated: true"); 193 case EnclosingMethodAttribute attr -> { 194 print("EnclosingMethod: #" + attr.enclosingClass().index() + ".#" 195 + attr.enclosingMethod().map(PoolEntry::index).orElse(0)); 196 tab(); 197 print("// " + getJavaName(attr.enclosingClass().asInternalName())); 198 if (attr.enclosingMethod().isPresent()) 199 print("." + attr.enclosingMethod().get().name().stringValue()); 200 println(); 201 } 202 case ExceptionsAttribute attr -> { 203 println("Exceptions:"); 204 indent(+1); 205 print("throws "); 206 var exc = attr.exceptions(); 207 for (int i = 0; i < exc.size(); i++) { 208 if (i > 0) 209 print(", "); 210 print(getJavaName(exc.get(i).asInternalName())); 211 } 212 println(); 213 indent(-1); 214 } 215 case InnerClassesAttribute attr -> { 216 boolean first = true; 217 for (var info : attr.classes()) { 218 //access 219 int access_flags = info.flagsMask(); 220 if (options.checkAccess(access_flags)) { 221 if (first) { 222 println("InnerClasses:"); 223 indent(+1); 224 first = false; 225 } 226 var flagSet = maskToAccessFlagsReportUnknown(access_flags, AccessFlag.Location.INNER_CLASS, cffv); 227 for (var flag : flagSet) { 228 if (flag.sourceModifier() && (flag != AccessFlag.ABSTRACT 229 || !info.has(AccessFlag.INTERFACE))) { 230 print(Modifier.toString(flag.mask()) + " "); 231 } 232 } 233 if (info.innerName().isPresent()) { 234 print("#" + info.innerName().get().index() + "= "); 235 } 236 print("#" + info.innerClass().index()); 237 if (info.outerClass().isPresent()) { 238 print(" of #" + info.outerClass().get().index()); 239 } 240 print(";"); 241 tab(); 242 print("// "); 243 if (info.innerName().isPresent()) { 244 print(info.innerName().get().stringValue() + "="); 245 } 246 constantWriter.write(info.innerClass().index()); 247 if (info.outerClass().isPresent()) { 248 print(" of "); 249 constantWriter.write(info.outerClass().get().index()); 250 } 251 println(); 252 if (options.verbose) { 253 indent(1); 254 classWriter.writeList(String.format("flags: (0x%04x) ", access_flags), 255 flagSet, "\n"); 256 indent(-1); 257 } 258 } 259 } 260 if (!first) 261 indent(-1); 262 } 263 case LineNumberTableAttribute attr -> { 264 println("LineNumberTable:"); 265 indent(+1); 266 for (var entry: attr.lineNumbers()) { 267 println("line " + entry.lineNumber() + ": " + entry.startPc()); 268 } 269 indent(-1); 270 } 271 case LocalVariableTableAttribute attr -> { 272 println("LocalVariableTable:"); 273 indent(+1); 274 println("Start Length Slot Name Signature"); 275 for (var entry : attr.localVariables()) { 276 println(String.format("%5d %7d %5d %5s %s", 277 entry.startPc(), entry.length(), entry.slot(), 278 constantWriter.stringValue(entry.name()), 279 constantWriter.stringValue(entry.type()))); 280 } 281 indent(-1); 282 } 283 case LocalVariableTypeTableAttribute attr -> { 284 println("LocalVariableTypeTable:"); 285 indent(+1); 286 println("Start Length Slot Name Signature"); 287 for (var entry : attr.localVariableTypes()) { 288 println(String.format("%5d %7d %5d %5s %s", 289 entry.startPc(), entry.length(), entry.slot(), 290 constantWriter.stringValue(entry.name()), 291 constantWriter.stringValue(entry.signature()))); 292 } 293 indent(-1); 294 } 295 case NestHostAttribute attr -> { 296 print("NestHost: "); 297 constantWriter.write(attr.nestHost().index()); 298 println(); 299 } 300 case MethodParametersAttribute attr -> { 301 final String header = String.format(format, "Name", "Flags"); 302 println("MethodParameters:"); 303 indent(+1); 304 println(header); 305 for (var entry : attr.parameters()) { 306 String namestr = 307 entry.name().isPresent() ? 308 constantWriter.stringValue(entry.name().get()) : "<no name>"; 309 String flagstr = 310 (entry.has(AccessFlag.FINAL) ? "final " : "") + 311 (entry.has(AccessFlag.MANDATED) ? "mandated " : "") + 312 (entry.has(AccessFlag.SYNTHETIC) ? "synthetic" : ""); 313 println(String.format(format, namestr, flagstr)); 314 } 315 indent(-1); 316 } 317 case ModuleAttribute attr -> { 318 println("Module:"); 319 indent(+1); 320 321 print("#" + attr.moduleName().index()); 322 print(","); 323 print(String.format("%x", attr.moduleFlagsMask())); 324 tab(); 325 print("// " + constantWriter.stringValue(attr.moduleName())); 326 if (attr.has(AccessFlag.OPEN)) 327 print(" ACC_OPEN"); 328 if (attr.has(AccessFlag.MANDATED)) 329 print(" ACC_MANDATED"); 330 if (attr.has(AccessFlag.SYNTHETIC)) 331 print(" ACC_SYNTHETIC"); 332 println(); 333 var ver = attr.moduleVersion(); 334 print("#" + ver.map(Utf8Entry::index).orElse(0)); 335 if (ver.isPresent()) { 336 tab(); 337 print("// " + constantWriter.stringValue(ver.get())); 338 } 339 println(); 340 { 341 var entries = attr.requires(); 342 print(entries.size()); 343 tab(); 344 println("// " + "requires"); 345 indent(+1); 346 for (var e: entries) { 347 print("#" + e.requires().index() + "," 348 + String.format("%x", e.requiresFlagsMask())); 349 tab(); 350 print("// " + constantWriter.stringValue(e.requires())); 351 if (e.has(AccessFlag.TRANSITIVE)) 352 print(" ACC_TRANSITIVE"); 353 if (e.has(AccessFlag.STATIC_PHASE)) 354 print(" ACC_STATIC_PHASE"); 355 if (e.has(AccessFlag.SYNTHETIC)) 356 print(" ACC_SYNTHETIC"); 357 if (e.has(AccessFlag.MANDATED)) 358 print(" ACC_MANDATED"); 359 println(); 360 var reqVer = e.requiresVersion(); 361 print("#" + reqVer.map(Utf8Entry::index).orElse(0)); 362 if (reqVer.isPresent()) { 363 tab(); 364 print("// " + constantWriter.stringValue(reqVer.get())); 365 } 366 println(); 367 } 368 indent(-1); 369 } 370 { 371 var entries = attr.exports(); 372 print(entries.size()); 373 tab(); 374 println("// exports"); 375 indent(+1); 376 for (var e: entries) { 377 printExportOpenEntry(e.exportedPackage().index(), 378 e.exportsFlagsMask(), e.exportsTo()); 379 } 380 indent(-1); 381 } 382 { 383 var entries = attr.opens(); 384 print(entries.size()); 385 tab(); 386 println("// opens"); 387 indent(+1); 388 for (var e: entries) { 389 printExportOpenEntry(e.openedPackage().index(), 390 e.opensFlagsMask(), e.opensTo()); 391 } 392 indent(-1); 393 } 394 { 395 var entries = attr.uses(); 396 print(entries.size()); 397 tab(); 398 println("// " + "uses"); 399 indent(+1); 400 for (var e: entries) { 401 print("#" + e.index()); 402 tab(); 403 println("// " + constantWriter.stringValue(e)); 404 } 405 indent(-1); 406 } 407 { 408 var entries = attr.provides(); 409 print(entries.size()); 410 tab(); 411 println("// " + "provides"); 412 indent(+1); 413 for (var e: entries) { 414 print("#" + e.provides().index()); 415 tab(); 416 print("// "); 417 print(constantWriter.stringValue(e.provides())); 418 println(" with ... " + e.providesWith().size()); 419 indent(+1); 420 for (var with : e.providesWith()) { 421 print("#" + with.index()); 422 tab(); 423 println("// ... with " + constantWriter.stringValue(with)); 424 } 425 indent(-1); 426 } 427 indent(-1); 428 } 429 indent(-1); 430 } 431 case ModuleHashesAttribute attr -> { 432 println("ModuleHashes:"); 433 indent(+1); 434 print("algorithm: #" + attr.algorithm().index()); 435 tab(); 436 println("// " + attr.algorithm().stringValue()); 437 print(attr.hashes().size()); 438 tab(); 439 println("// hashes"); 440 for (var e : attr.hashes()) { 441 print("#" + e.moduleName().index()); 442 tab(); 443 println("// " + e.moduleName().name().stringValue()); 444 println("hash_length: " + e.hash().length); 445 println("hash: [" + toHex(e.hash()) + "]"); 446 } 447 indent(-1); 448 } 449 case ModuleMainClassAttribute attr -> { 450 print("ModuleMainClass: #" + attr.mainClass().index()); 451 tab(); 452 print("// " + getJavaName(attr.mainClass().asInternalName())); 453 println(); 454 } 455 case ModulePackagesAttribute attr -> { 456 println("ModulePackages: "); 457 indent(+1); 458 for (var p : attr.packages()) { 459 print("#" + p.index()); 460 tab(); 461 println("// " + getJavaName(p.name().stringValue())); 462 } 463 indent(-1); 464 } 465 case ModuleResolutionAttribute attr -> { 466 println("ModuleResolution:"); 467 indent(+1); 468 print(String.format("%x", attr.resolutionFlags())); 469 tab(); 470 print("// "); 471 int flags = attr.resolutionFlags(); 472 if ((flags & DO_NOT_RESOLVE_BY_DEFAULT) != 0) 473 print(" DO_NOT_RESOLVE_BY_DEFAULT"); 474 if ((flags & WARN_DEPRECATED) != 0) 475 print(" WARN_DEPRECATED"); 476 if ((flags & WARN_DEPRECATED_FOR_REMOVAL) != 0) 477 print(" WARN_DEPRECATED_FOR_REMOVAL"); 478 if ((flags & WARN_INCUBATING) != 0) 479 print(" WARN_INCUBATING"); 480 println(); 481 indent(-1); 482 } 483 case ModuleTargetAttribute attr -> { 484 println("ModuleTarget:"); 485 indent(+1); 486 print("target_platform: #" + attr.targetPlatform().index()); 487 tab(); 488 println("// " + attr.targetPlatform().stringValue()); 489 indent(-1); 490 } 491 case NestMembersAttribute attr -> { 492 println("NestMembers:"); 493 indent(+1); 494 for (var m : attr.nestMembers()) { 495 println(constantWriter.stringValue(m)); 496 } 497 indent(-1); 498 } 499 case RecordAttribute attr -> { 500 println("Record:"); 501 indent(+1); 502 for (var componentInfo : attr.components()) { 503 var sigAttr = componentInfo.findAttribute(Attributes.signature()); 504 print(getJavaName( 505 new ClassWriter.SignaturePrinter(options.verbose).print( 506 sigAttr.map(SignatureAttribute::asTypeSignature) 507 .orElse(Signature.of( 508 componentInfo.descriptorSymbol()))))); 509 print(" "); 510 print(componentInfo.name().stringValue()); 511 print(";"); 512 println(); 513 indent(+1); 514 if (options.showDescriptors) { 515 println("descriptor: " + componentInfo.descriptor().stringValue()); 516 } 517 if (options.showAllAttrs) { 518 write(componentInfo.attributes(), cffv); 519 println(); 520 } 521 indent(-1); 522 } 523 indent(-1); 524 } 525 case RuntimeVisibleAnnotationsAttribute attr -> 526 printAnnotations("RuntimeVisibleAnnotations:", attr.annotations()); 527 case RuntimeInvisibleAnnotationsAttribute attr -> 528 printAnnotations("RuntimeInvisibleAnnotations:", attr.annotations()); 529 case RuntimeVisibleTypeAnnotationsAttribute attr -> 530 printTypeAnnotations("RuntimeVisibleTypeAnnotations:", 531 attr.annotations(), lr); 532 case RuntimeInvisibleTypeAnnotationsAttribute attr -> 533 printTypeAnnotations("RuntimeInvisibleTypeAnnotations:", 534 attr.annotations(), lr); 535 case RuntimeVisibleParameterAnnotationsAttribute attr -> 536 printParameterAnnotations("RuntimeVisibleParameterAnnotations:", 537 attr.parameterAnnotations()); 538 case RuntimeInvisibleParameterAnnotationsAttribute attr -> 539 printParameterAnnotations("RuntimeInvisibleParameterAnnotations:", 540 attr.parameterAnnotations()); 541 case PermittedSubclassesAttribute attr -> { 542 println("PermittedSubclasses:"); 543 indent(+1); 544 for (var sc : attr.permittedSubclasses()) { 545 println(constantWriter.stringValue(sc)); 546 } 547 indent(-1); 548 } 549 case LoadableDescriptorsAttribute attr -> { 550 println("LoadableDescriptors:"); 551 indent(+1); 552 for (var sc : attr.loadableDescriptors()) { 553 println(constantWriter.stringValue(sc)); 554 } 555 indent(-1); 556 } 557 case SignatureAttribute attr -> { 558 print("Signature: #" + attr.signature().index()); 559 tab(); 560 println("// " + attr.signature().stringValue()); 561 } 562 case SourceDebugExtensionAttribute attr -> { 563 println("SourceDebugExtension:"); 564 indent(+1); 565 for (String s: new String(attr.contents(), StandardCharsets.UTF_8) 566 .split("[\r\n]+")) { 567 println(s); 568 } 569 indent(-1); 570 } 571 case SourceFileAttribute attr -> 572 println("SourceFile: \"" + attr.sourceFile().stringValue() + "\""); 573 case SourceIDAttribute attr -> 574 constantWriter.write(attr.sourceId().index()); 575 case StackMapTableAttribute attr -> { 576 var entries = attr.entries(); 577 println("StackMapTable: number_of_entries = " + entries.size()); 578 indent(+1); 579 int lastOffset = -1; 580 for (var frame : entries) { 581 int frameType = frame.frameType(); 582 if (frameType < 64) { 583 printHeader(frameType, "/* same */"); 584 } else if (frameType < 128) { 585 printHeader(frameType, "/* same_locals_1_stack_item */"); 586 indent(+1); 587 printMap("stack", frame.stack(), lr); 588 indent(-1); 589 } else { 590 int offsetDelta = lr.labelToBci(frame.target()) - lastOffset - 1; 591 switch (frameType) { 592 case 246 -> { 593 printHeader(frameType, "/* early_larval */"); 594 indent(+1); 595 println("number of unset_fields = " + frame.unsetFields().size()); 596 indent(+1); 597 for (NameAndTypeEntry field : frame.unsetFields()) { 598 print("unset_field = #"); 599 constantWriter.write(field.index()); 600 println(); 601 } 602 // temporary: print the nested contents of early larval 603 indent(+1); 604 println("offset_delta = " + offsetDelta); 605 printMap("locals", frame.locals(), lr); 606 printMap("stack", frame.stack(), lr); 607 indent(-1); 608 indent(-1); 609 indent(-1); 610 } 611 case 247 -> { 612 printHeader(frameType, "/* same_locals_1_stack_item_entry_extended */"); 613 indent(+1); 614 println("offset_delta = " + offsetDelta); 615 printMap("stack", frame.stack(), lr); 616 indent(-1); 617 } 618 case 248, 249, 250 -> { 619 printHeader(frameType, "/* chop */"); 620 indent(+1); 621 println("offset_delta = " + offsetDelta); 622 indent(-1); 623 } 624 case 251 -> { 625 printHeader(frameType, "/* same_entry_extended */"); 626 indent(+1); 627 println("offset_delta = " + offsetDelta); 628 indent(-1); 629 } 630 case 252, 253, 254 -> { 631 printHeader(frameType, "/* append */"); 632 indent(+1); 633 println("offset_delta = " + offsetDelta); 634 var locals = frame.locals(); 635 printMap("locals", locals.subList(locals.size() 636 - frameType + 251, locals.size()), lr); 637 indent(-1); 638 } 639 case 255 -> { 640 printHeader(frameType, "/* full_entry */"); 641 indent(+1); 642 println("offset_delta = " + offsetDelta); 643 printMap("locals", frame.locals(), lr); 644 printMap("stack", frame.stack(), lr); 645 indent(-1); 646 } 647 } 648 } 649 lastOffset = lr.labelToBci(frame.target()); 650 } 651 indent(-1); 652 } 653 case SyntheticAttribute attr -> 654 println("Synthetic: true"); 655 default -> {} 656 } 657 } 658 659 //ToDo move somewhere to Bytecode API 660 public static final int DO_NOT_RESOLVE_BY_DEFAULT = 0x0001; 661 public static final int WARN_DEPRECATED = 0x0002; 662 public static final int WARN_DEPRECATED_FOR_REMOVAL = 0x0004; 663 public static final int WARN_INCUBATING = 0x0008; 664 665 private static final String format = "%-31s%s"; 666 667 protected void printExportOpenEntry(int index, int flags, List<ModuleEntry> to_index) { 668 print("#" + index + "," + String.format("%x", flags)); 669 tab(); 670 print("// "); 671 print(constantWriter.stringValue(index)); 672 if ((flags & ACC_MANDATED) != 0) 673 print(" ACC_MANDATED"); 674 if ((flags & ACC_SYNTHETIC) != 0) 675 print(" ACC_SYNTHETIC"); 676 if (to_index.size() == 0) { 677 println(); 678 } else { 679 println(" to ... " + to_index.size()); 680 indent(+1); 681 for (var to: to_index) { 682 print("#" + to.index()); 683 tab(); 684 println("// ... to " + constantWriter.stringValue(to)); 685 } 686 indent(-1); 687 } 688 } 689 690 private void printAnnotations(String message, List<? extends Annotation> anno) { 691 println(message); 692 indent(+1); 693 for (int i = 0; i < anno.size(); i++) { 694 print(i + ": "); 695 annotationWriter.write(anno.get(i)); 696 println(); 697 } 698 indent(-1); 699 } 700 701 private void printTypeAnnotations(String message, 702 List<? extends TypeAnnotation> anno, CodeAttribute lr) { 703 println(message); 704 indent(+1); 705 for (int i = 0; i < anno.size(); i++) { 706 print(i + ": "); 707 annotationWriter.write(anno.get(i), lr); 708 println(); 709 } 710 indent(-1); 711 } 712 713 private void printParameterAnnotations(String message, List<List<Annotation>> paramsAnno) { 714 println(message); 715 indent(+1); 716 for (int param = 0; param < paramsAnno.size(); param++) { 717 println("parameter " + param + ": "); 718 indent(+1); 719 var annos = paramsAnno.get(param); 720 for (int i = 0; i < annos.size(); i++) { 721 print(i + ": "); 722 annotationWriter.write(annos.get(i)); 723 println(); 724 } 725 indent(-1); 726 } 727 indent(-1); 728 } 729 730 void printHeader(int frameType, String extra) { 731 print("frame_type = " + frameType + " "); 732 println(extra); 733 } 734 735 void printMap(String name, List<VerificationTypeInfo> map, CodeAttribute lr) { 736 print(name + " = ["); 737 for (int i = 0; i < map.size(); i++) { 738 var info = map.get(i); 739 switch (info) { 740 case ObjectVerificationTypeInfo obj -> { 741 print(" "); 742 constantWriter.write(obj.className().index()); 743 } 744 case UninitializedVerificationTypeInfo u -> { 745 print(" uninitialized " + lr.labelToBci(u.newTarget())); 746 } 747 case SimpleVerificationTypeInfo s -> 748 print(" " + mapTypeName(s)); 749 } 750 print(i == (map.size() - 1) ? " " : ","); 751 } 752 println("]"); 753 } 754 755 String mapTypeName(SimpleVerificationTypeInfo type) { 756 return switch (type) { 757 case TOP -> "top"; 758 case INTEGER -> "int"; 759 case FLOAT -> "float"; 760 case LONG -> "long"; 761 case DOUBLE -> "double"; 762 case NULL -> "null"; 763 case UNINITIALIZED_THIS -> "this"; 764 }; 765 } 766 767 static String getJavaName(String name) { 768 return name.replace('/', '.'); 769 } 770 771 String toHex(byte b, int w) { 772 return toHex(b & 0xff, w); 773 } 774 775 static String toHex(int i) { 776 return Integer.toString(i, 16).toUpperCase(Locale.US); 777 } 778 779 static String toHex(int i, int w) { 780 String s = Integer.toHexString(i).toUpperCase(Locale.US); 781 while (s.length() < w) 782 s = "0" + s; 783 return s; 784 } 785 786 static String toHex(byte[] ba) { 787 StringBuilder sb = new StringBuilder(ba.length); 788 for (byte b: ba) { 789 sb.append(String.format("%02x", b & 0xff)); 790 } 791 return sb.toString(); 792 } 793 794 private final ClassWriter classWriter; 795 private final AnnotationWriter annotationWriter; 796 private final CodeWriter codeWriter; 797 private final ConstantWriter constantWriter; 798 private final Options options; 799 }