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