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.AccessFlags; 29 import java.lang.classfile.Attributes; 30 import java.lang.classfile.ClassFile; 31 import java.lang.classfile.ClassHierarchyResolver; 32 import java.lang.classfile.ClassModel; 33 import java.lang.classfile.ClassSignature; 34 import java.lang.classfile.FieldModel; 35 import java.lang.classfile.MethodModel; 36 import java.lang.classfile.MethodSignature; 37 import java.lang.classfile.Signature; 38 import java.lang.classfile.attribute.CodeAttribute; 39 import java.lang.classfile.attribute.SignatureAttribute; 40 import java.lang.classfile.constantpool.ClassEntry; 41 import java.lang.classfile.constantpool.ConstantValueEntry; 42 import java.lang.classfile.constantpool.PoolEntry; 43 import java.lang.constant.ClassDesc; 44 import java.lang.reflect.AccessFlag; 45 import java.lang.reflect.ClassFileFormatVersion; 46 import java.lang.reflect.Modifier; 47 import java.net.URI; 48 import java.text.DateFormat; 49 import java.util.ArrayList; 50 import java.util.Collection; 51 import java.util.Date; 52 import java.util.EnumSet; 53 import java.util.LinkedHashSet; 54 import java.util.List; 55 import java.util.Set; 56 57 import static java.lang.classfile.ClassFile.*; 58 59 /* 60 * The main javap class to write the contents of a class file as text. 61 * 62 * <p><b>This is NOT part of any supported API. 63 * If you write code that depends on this, you do so at your own risk. 64 * This code and its internal interfaces are subject to change or 65 * deletion without notice.</b> 66 */ 67 public class ClassWriter extends BasicWriter { 68 static ClassWriter instance(Context context) { 69 ClassWriter instance = context.get(ClassWriter.class); 70 if (instance == null) 71 instance = new ClassWriter(context); 72 return instance; 73 } 74 75 protected ClassWriter(Context context) { 76 super(context); 77 context.put(ClassWriter.class, this); 78 options = Options.instance(context); 79 attrWriter = AttributeWriter.instance(context); 80 codeWriter = CodeWriter.instance(context); 81 constantWriter = ConstantWriter.instance(context); 82 sigPrinter = new SignaturePrinter(options.verbose); 83 } 84 85 void setDigest(String name, byte[] digest) { 86 this.digestName = name; 87 this.digest = digest; 88 } 89 90 void setFile(URI uri) { 91 this.uri = uri; 92 } 93 94 void setFileSize(int size) { 95 this.size = size; 96 } 97 98 void setLastModified(long lastModified) { 99 this.lastModified = lastModified; 100 } 101 102 protected ClassModel getClassModel() { 103 return classModel; 104 } 105 106 protected void setClassFile(ClassModel cm) { 107 classModel = cm; 108 } 109 110 protected MethodModel getMethod() { 111 return method; 112 } 113 114 protected void setMethod(MethodModel m) { 115 method = m; 116 } 117 118 protected ClassFileFormatVersion cffv() { 119 var major = classModel.majorVersion(); 120 if (major < JAVA_1_VERSION || major > ClassFile.latestMajorVersion()) 121 return null; 122 if (major >= JAVA_12_VERSION && classModel.minorVersion() != 0) { 123 return null; 124 } 125 return ClassFileFormatVersion.fromMajor(major); 126 } 127 128 public boolean write(ClassModel cm) { 129 errorReported = false; 130 setClassFile(cm); 131 132 if (options.sysInfo || options.verbose) { 133 if (uri != null) { 134 if (uri.getScheme().equals("file")) 135 println("Classfile " + uri.getPath()); 136 else 137 println("Classfile " + uri); 138 } 139 indent(+1); 140 if (lastModified != -1) { 141 Date lm = new Date(lastModified); 142 DateFormat df = DateFormat.getDateInstance(); 143 if (size > 0) { 144 println("Last modified " + df.format(lm) + "; size " + size 145 + " bytes"); 146 } else { 147 println("Last modified " + df.format(lm)); 148 } 149 } else if (size > 0) { 150 println("Size " + size + " bytes"); 151 } 152 if (digestName != null && digest != null) { 153 StringBuilder sb = new StringBuilder(); 154 for (byte b: digest) 155 sb.append(String.format("%02x", b)); 156 println(digestName + " checksum " + sb); 157 } 158 } 159 160 cm.findAttribute(Attributes.sourceFile()).ifPresent(sfa -> 161 println("Compiled from \"" + sfa.sourceFile().stringValue() + "\"")); 162 163 if (options.sysInfo || options.verbose) { 164 indent(-1); 165 } 166 167 writeModifiers(getClassModifiers(cm.flags())); 168 169 if ((classModel.flags().flagsMask() & ACC_MODULE) != 0) { 170 var attr = classModel.findAttribute(Attributes.module()); 171 if (attr.isPresent()) { 172 var modAttr = attr.get(); 173 if ((modAttr.moduleFlagsMask() & ACC_OPEN) != 0) { 174 print("open "); 175 } 176 print("module "); 177 print(() -> modAttr.moduleName().name().stringValue()); 178 if (modAttr.moduleVersion().isPresent()) { 179 print("@"); 180 print(() -> modAttr.moduleVersion().get().stringValue()); 181 } 182 } else { 183 // fallback for malformed class files 184 print("class "); 185 print(() -> getJavaName(classModel.thisClass().asInternalName())); 186 } 187 } else { 188 if ((classModel.flags().flagsMask() & ACC_INTERFACE) == 0) 189 print("class "); 190 else 191 print("interface "); 192 193 print(() -> getJavaName(classModel.thisClass().asInternalName())); 194 } 195 196 try { 197 var sigAttr = classModel.findAttribute(Attributes.signature()).orElse(null); 198 if (sigAttr == null) { 199 // use info from class file header 200 if ((classModel.flags().flagsMask() & ACC_INTERFACE) == 0 201 && classModel.superclass().isPresent()) { 202 String sn = getJavaName(classModel.superclass().get().asInternalName()); 203 if (!sn.equals("java.lang.Object")) { 204 print(" extends "); 205 print(sn); 206 } 207 } 208 var interfaces = classModel.interfaces(); 209 for (int i = 0; i < interfaces.size(); i++) { 210 print(i == 0 ? ((classModel.flags().flagsMask() & ACC_INTERFACE) == 0 211 ? " implements " : " extends ") : ","); 212 print(getJavaName(interfaces.get(i).asInternalName())); 213 } 214 } else { 215 var t = sigAttr.asClassSignature(); 216 print(sigPrinter.print(t, (classModel.flags().flagsMask() & ACC_INTERFACE) != 0)); 217 } 218 } catch (IllegalArgumentException e) { 219 report(e); 220 } 221 222 if (options.verbose) { 223 println(); 224 indent(+1); 225 println("minor version: " + classModel.minorVersion()); 226 println("major version: " + classModel.majorVersion()); 227 writeList(String.format("flags: (0x%04x) ", cm.flags().flagsMask()), 228 getClassFlags(cm.flags()), "\n"); 229 print("this_class: #");print(() -> classModel.thisClass().index()); 230 tab(); 231 print(() -> "// " + classModel.thisClass().asInternalName()); 232 println(); 233 print("super_class: #");print(() -> classModel.superclass() 234 .map(ClassEntry::index).orElse(0)); 235 try { 236 if (classModel.superclass().isPresent()) { 237 tab(); 238 print(() -> "// " + classModel.superclass().get().asInternalName()); 239 } 240 } catch (IllegalArgumentException e) { 241 report(e); 242 } 243 println(); 244 print("interfaces: ");print(() -> classModel.interfaces().size()); 245 print(", fields: " + classModel.fields().size()); 246 print(", methods: " + classModel.methods().size()); 247 println(", attributes: " + classModel.attributes().size()); 248 indent(-1); 249 constantWriter.writeConstantPool(); 250 } else { 251 print(" "); 252 } 253 254 println("{"); 255 indent(+1); 256 if ((cm.flags().flagsMask() & ACC_MODULE) != 0 && !options.verbose) { 257 writeDirectives(); 258 } 259 writeFields(); 260 writeMethods(); 261 indent(-1); 262 println("}"); 263 264 if (options.verbose) { 265 attrWriter.write(classModel.attributes(), cffv()); 266 } 267 268 if (options.verify) { 269 var vErrors = VERIFIER.verify(classModel); 270 if (!vErrors.isEmpty()) { 271 println(); 272 for (var ve : vErrors) { 273 println(ve.getMessage()); 274 } 275 errorReported = true; 276 } 277 } 278 return !errorReported; 279 } 280 // where 281 282 private static final ClassFile VERIFIER = ClassFile.of(ClassHierarchyResolverOption.of( 283 ClassHierarchyResolver.defaultResolver().orElse(new ClassHierarchyResolver() { 284 @Override 285 public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { 286 // mark all unresolved classes as interfaces to exclude them from assignability verification 287 return ClassHierarchyInfo.ofInterface(); 288 } 289 }))); 290 291 final SignaturePrinter sigPrinter; 292 293 public static record SignaturePrinter(boolean verbose) { 294 295 public String print(ClassSignature cs, boolean isInterface) { 296 var sb = new StringBuilder(); 297 print(sb, cs.typeParameters()); 298 if (isInterface) { 299 String sep = " extends "; 300 for (var is : cs.superinterfaceSignatures()) { 301 sb.append(sep); 302 print(sb, is); 303 sep = ", "; 304 } 305 } else { 306 if (cs.superclassSignature() != null 307 && (verbose || !isObject(cs.superclassSignature()))) { 308 sb.append(" extends "); 309 print(sb, cs.superclassSignature()); 310 } 311 String sep = " implements "; 312 for (var is : cs.superinterfaceSignatures()) { 313 sb.append(sep); 314 print(sb, is); 315 sep = ", "; 316 } 317 } 318 return sb.toString(); 319 } 320 321 public String print(Signature sig) { 322 var sb = new StringBuilder(); 323 print(sb, sig); 324 return sb.toString(); 325 } 326 327 public String printTypeParams(List<Signature.TypeParam> tps) { 328 var sb = new StringBuilder(); 329 print(sb, tps); 330 return sb.toString(); 331 } 332 333 public String printList(String prefix, List<? extends Signature> args, 334 String postfix) { 335 var sb = new StringBuilder(); 336 sb.append(prefix); 337 String sep = ""; 338 for (var arg : args) { 339 sb.append(sep); 340 print(sb, arg); 341 sep = ", "; 342 } 343 return sb.append(postfix).toString(); 344 } 345 346 private boolean isObject(Signature sig) { 347 return (sig instanceof Signature.ClassTypeSig cts) 348 && cts.outerType().isEmpty() 349 && cts.className().equals("java/lang/Object") 350 && (cts.typeArgs().isEmpty()); 351 } 352 353 private void print(StringBuilder sb, List<Signature.TypeParam> tps) { 354 if (!tps.isEmpty()) { 355 sb.append('<'); 356 String sep = ""; 357 for (var tp : tps) { 358 sb.append(sep).append(tp.identifier()); 359 sep = " extends "; 360 if (tp.classBound().isPresent() 361 && (verbose || !isObject(tp.classBound().get()))) { 362 sb.append(sep); 363 print(sb, tp.classBound().get()); 364 sep = " & "; 365 } 366 for (var bound: tp.interfaceBounds()) { 367 sb.append(sep); 368 print(sb, bound); 369 sep = " & "; 370 } 371 sep = ", "; 372 } 373 sb.append('>'); 374 } 375 } 376 377 private void print(StringBuilder sb, Signature sig) { 378 if (sig instanceof Signature.BaseTypeSig bts) { 379 sb.append(ClassDesc.ofDescriptor("" + bts.baseType()).displayName()); 380 } else if (sig instanceof Signature.ClassTypeSig cts) { 381 if (cts.outerType().isPresent()) { 382 print(sb, cts.outerType().get()); 383 sb.append("."); 384 } 385 sb.append(getJavaName(cts.className())); 386 if (!cts.typeArgs().isEmpty()) { 387 String sep = ""; 388 sb.append('<'); 389 for (var ta : cts.typeArgs()) { 390 sb.append(sep); 391 print(sb, ta); 392 sep = ", "; 393 } 394 sb.append('>'); 395 } 396 } else if (sig instanceof Signature.TypeVarSig tvs) { 397 sb.append(tvs.identifier()); 398 } else if (sig instanceof Signature.ArrayTypeSig ats) { 399 print(sb, ats.componentSignature()); 400 sb.append("[]"); 401 } 402 } 403 404 private void print(StringBuilder sb, Signature.TypeArg ta) { 405 switch (ta) { 406 case Signature.TypeArg.Unbounded _ -> sb.append('?'); 407 case Signature.TypeArg.Bounded bta -> { 408 switch (bta.wildcardIndicator()) { 409 case NONE -> print(sb, bta.boundType()); 410 case EXTENDS -> { 411 sb.append("? extends "); 412 print(sb, bta.boundType()); 413 } 414 case SUPER -> { 415 sb.append("? super "); 416 print(sb, bta.boundType()); 417 } 418 } 419 } 420 } 421 } 422 } 423 424 protected void writeFields() { 425 for (var f: classModel.fields()) { 426 writeField(f); 427 } 428 } 429 430 protected void writeField(FieldModel f) { 431 if (!options.checkAccess(f.flags().flagsMask())) 432 return; 433 434 var flags = f.flags(); 435 writeModifiers(flagsReportUnknown(flags, cffv()).stream().filter(fl -> fl.sourceModifier()) 436 .map(fl -> Modifier.toString(fl.mask())).toList()); 437 print(() -> sigPrinter.print( 438 f.findAttribute(Attributes.signature()) 439 .map(SignatureAttribute::asTypeSignature) 440 .orElseGet(() -> Signature.of(f.fieldTypeSymbol())))); 441 print(" "); 442 print(() -> f.fieldName().stringValue()); 443 if (options.showConstants) { 444 var a = f.findAttribute(Attributes.constantValue()); 445 if (a.isPresent()) { 446 print(" = "); 447 var cv = a.get(); 448 print(() -> getConstantValue(f.fieldTypeSymbol(), cv.constant())); 449 } 450 } 451 print(";"); 452 println(); 453 454 indent(+1); 455 456 boolean showBlank = false; 457 458 if (options.showDescriptors) { 459 print("descriptor: ");println(() -> f.fieldType().stringValue()); 460 } 461 462 if (options.verbose) 463 writeList(String.format("flags: (0x%04x) ", flags.flagsMask()), 464 flagsReportUnknown(flags, cffv()).stream().map(fl -> "ACC_" + fl.name()).toList(), 465 "\n"); 466 467 if (options.showAllAttrs) { 468 attrWriter.write(f.attributes(), cffv()); 469 showBlank = true; 470 } 471 472 indent(-1); 473 474 if (showBlank || options.showDisassembled || options.showLineAndLocalVariableTables) 475 println(); 476 } 477 478 protected void writeMethods() { 479 for (MethodModel m: classModel.methods()) 480 writeMethod(m); 481 setPendingNewline(false); 482 } 483 484 private static final int DEFAULT_ALLOWED_MAJOR_VERSION = 52; 485 private static final int DEFAULT_ALLOWED_MINOR_VERSION = 0; 486 487 protected void writeMethod(MethodModel m) { 488 if (!options.checkAccess(m.flags().flagsMask())) 489 return; 490 491 method = m; 492 493 int flags = m.flags().flagsMask(); 494 495 var modifiers = new ArrayList<String>(); 496 for (var f : flagsReportUnknown(m.flags(), cffv())) 497 if (f.sourceModifier()) modifiers.add(Modifier.toString(f.mask())); 498 499 String name = "???"; 500 try { 501 name = m.methodName().stringValue(); 502 } catch (IllegalArgumentException e) { 503 report(e); 504 } 505 506 if ((classModel.flags().flagsMask() & ACC_INTERFACE) != 0 && 507 ((flags & ACC_ABSTRACT) == 0) && !name.equals("<clinit>")) { 508 if (classModel.majorVersion() > DEFAULT_ALLOWED_MAJOR_VERSION || 509 (classModel.majorVersion() == DEFAULT_ALLOWED_MAJOR_VERSION 510 && classModel.minorVersion() >= DEFAULT_ALLOWED_MINOR_VERSION)) { 511 if ((flags & (ACC_STATIC | ACC_PRIVATE)) == 0) { 512 modifiers.add("default"); 513 } 514 } 515 } 516 writeModifiers(modifiers); 517 518 try { 519 var sigAttr = m.findAttribute(Attributes.signature()); 520 MethodSignature d; 521 if (sigAttr.isEmpty()) { 522 d = MethodSignature.parseFrom(m.methodType().stringValue()); 523 } else { 524 d = sigAttr.get().asMethodSignature(); 525 } 526 527 if (!d.typeParameters().isEmpty()) { 528 print(sigPrinter.printTypeParams(d.typeParameters()) + " "); 529 } 530 switch (name) { 531 case "<init>": 532 print(getJavaName(classModel.thisClass().asInternalName())); 533 print(getJavaParameterTypes(d, flags)); 534 break; 535 case "<clinit>": 536 print("{}"); 537 break; 538 default: 539 print(getJavaName(sigPrinter.print(d.result()))); 540 print(" "); 541 print(name); 542 print(getJavaParameterTypes(d, flags)); 543 break; 544 } 545 546 var e_attr = m.findAttribute(Attributes.exceptions()); 547 // if there are generic exceptions, there must be erased exceptions 548 if (e_attr.isPresent()) { 549 var exceptions = e_attr.get(); 550 print(" throws "); 551 if (d != null && !d.throwableSignatures().isEmpty()) { // use generic list if available 552 print(() -> sigPrinter.printList("", d.throwableSignatures(), "")); 553 } else { 554 var exNames = exceptions.exceptions(); 555 for (int i = 0; i < exNames.size(); i++) { 556 if (i > 0) 557 print(", "); 558 int ii = i; 559 print(() -> getJavaName(exNames.get(ii).asInternalName())); 560 } 561 } 562 } 563 } catch (IllegalArgumentException e) { 564 report(e); 565 } 566 567 println(";"); 568 569 indent(+1); 570 571 if (options.showDescriptors) { 572 print("descriptor: ");println(() -> m.methodType().stringValue()); 573 } 574 575 if (options.verbose) { 576 StringBuilder sb = new StringBuilder(); 577 String sep = ""; 578 sb.append(String.format("flags: (0x%04x) ", flags)); 579 for (var f : flagsReportUnknown(m.flags(), cffv())) { 580 sb.append(sep).append("ACC_").append(f.name()); 581 sep = ", "; 582 } 583 println(sb.toString()); 584 } 585 586 var code = (CodeAttribute)m.code().orElse(null); 587 588 if (options.showAllAttrs) { 589 attrWriter.write(m.attributes(), cffv()); 590 } else if (code != null && options.showDisassembled) { 591 codeWriter.writeMinimal(code); 592 } 593 594 indent(-1); 595 596 // set pendingNewline to write a newline before the next method (if any) 597 // if a separator is desired 598 setPendingNewline( 599 options.showDisassembled || 600 options.showAllAttrs || 601 options.showDescriptors || 602 options.showLineAndLocalVariableTables || 603 options.verbose); 604 } 605 606 void writeModifiers(Collection<String> items) { 607 for (Object item: items) { 608 print(item); 609 print(" "); 610 } 611 } 612 613 public static final int ACC_TRANSITIVE = 0x0020; 614 public static final int ACC_STATIC_PHASE = 0x0040; 615 616 void writeDirectives() { 617 var attr = classModel.findAttribute(Attributes.module()); 618 if (attr.isEmpty()) 619 return; 620 621 var m = attr.get(); 622 for (var entry: m.requires()) { 623 print("requires"); 624 if ((entry.requiresFlagsMask() & ACC_STATIC_PHASE) != 0) 625 print(" static"); 626 if ((entry.requiresFlagsMask() & ACC_TRANSITIVE) != 0) 627 print(" transitive"); 628 print(" "); 629 String mname; 630 print(entry.requires().name().stringValue()); 631 println(";"); 632 } 633 634 for (var entry: m.exports()) { 635 print("exports"); 636 print(" "); 637 print(entry.exportedPackage().name().stringValue().replace('/', '.')); 638 boolean first = true; 639 for (var mod: entry.exportsTo()) { 640 if (first) { 641 println(" to"); 642 indent(+1); 643 first = false; 644 } else { 645 println(","); 646 } 647 print(mod.name().stringValue()); 648 } 649 println(";"); 650 if (!first) 651 indent(-1); 652 } 653 654 for (var entry: m.opens()) { 655 print("opens"); 656 print(" "); 657 print(entry.openedPackage().name().stringValue().replace('/', '.')); 658 boolean first = true; 659 for (var mod: entry.opensTo()) { 660 if (first) { 661 println(" to"); 662 indent(+1); 663 first = false; 664 } else { 665 println(","); 666 } 667 print(mod.name().stringValue()); 668 } 669 println(";"); 670 if (!first) 671 indent(-1); 672 } 673 674 for (var entry: m.uses()) { 675 print("uses "); 676 print(entry.asInternalName().replace('/', '.')); 677 println(";"); 678 } 679 680 for (var entry: m.provides()) { 681 print("provides "); 682 print(entry.provides().asInternalName().replace('/', '.')); 683 boolean first = true; 684 for (var ce: entry.providesWith()) { 685 if (first) { 686 println(" with"); 687 indent(+1); 688 first = false; 689 } else { 690 println(","); 691 } 692 print(ce.asInternalName().replace('/', '.')); 693 } 694 println(";"); 695 if (!first) 696 indent(-1); 697 } 698 } 699 700 void writeList(String prefix, Collection<?> items, String suffix) { 701 print(prefix); 702 String sep = ""; 703 for (Object item: items) { 704 print(sep); 705 print(item); 706 sep = ", "; 707 } 708 print(suffix); 709 } 710 711 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) { 712 if (items != null && items.size() > 0) 713 writeList(prefix, items, suffix); 714 } 715 716 String adjustVarargs(int flags, String params) { 717 if ((flags & ACC_VARARGS) != 0) { 718 int i = params.lastIndexOf("[]"); 719 if (i > 0) 720 return params.substring(0, i) + "..." + params.substring(i+2); 721 } 722 723 return params; 724 } 725 726 String getJavaParameterTypes(MethodSignature d, int flags) { 727 return getJavaName(adjustVarargs(flags, 728 sigPrinter.printList("(", d.arguments(), ")"))); 729 } 730 731 static String getJavaName(String name) { 732 return name.replace('/', '.'); 733 } 734 735 /** 736 * Get the value of an entry in the constant pool as a Java constant. 737 * Characters and booleans are represented by CONSTANT_Intgere entries. 738 * Character and string values are processed to escape characters outside 739 * the basic printable ASCII set. 740 * @param d the descriptor, giving the expected type of the constant 741 * @param index the index of the value in the constant pool 742 * @return a printable string containing the value of the constant. 743 */ 744 String getConstantValue(ClassDesc d, ConstantValueEntry cpInfo) { 745 switch (cpInfo.tag()) { 746 case PoolEntry.TAG_INTEGER: { 747 var val = (Integer)cpInfo.constantValue(); 748 switch (d.descriptorString()) { 749 case "C": 750 // character 751 return getConstantCharValue((char)val.intValue()); 752 case "Z": 753 // boolean 754 return String.valueOf(val == 1); 755 default: 756 // other: assume integer 757 return String.valueOf(val); 758 } 759 } 760 case PoolEntry.TAG_STRING: 761 return getConstantStringValue(cpInfo.constantValue().toString()); 762 default: 763 return constantWriter.stringValue(cpInfo); 764 } 765 } 766 767 private String getConstantCharValue(char c) { 768 StringBuilder sb = new StringBuilder(); 769 sb.append('\''); 770 sb.append(esc(c, '\'')); 771 sb.append('\''); 772 return sb.toString(); 773 } 774 775 private String getConstantStringValue(String s) { 776 StringBuilder sb = new StringBuilder(); 777 sb.append("\""); 778 for (int i = 0; i < s.length(); i++) { 779 sb.append(esc(s.charAt(i), '"')); 780 } 781 sb.append("\""); 782 return sb.toString(); 783 } 784 785 private String esc(char c, char quote) { 786 if (32 <= c && c <= 126 && c != quote && c != '\\') 787 return String.valueOf(c); 788 else switch (c) { 789 case '\b': return "\\b"; 790 case '\n': return "\\n"; 791 case '\t': return "\\t"; 792 case '\f': return "\\f"; 793 case '\r': return "\\r"; 794 case '\\': return "\\\\"; 795 case '\'': return "\\'"; 796 case '\"': return "\\\""; 797 default: return String.format("\\u%04x", (int) c); 798 } 799 } 800 801 private Set<String> getClassModifiers(AccessFlags flags) { 802 var flagSet = flagsReportUnknown(flags, cffv()); 803 Set<AccessFlag> set; 804 if (flagSet.contains(AccessFlag.INTERFACE)) { 805 set = EnumSet.copyOf(flagSet); 806 set.remove(AccessFlag.ABSTRACT); 807 } else { 808 set = flagSet; 809 } 810 return getModifiers(set); 811 } 812 813 private static Set<String> getModifiers(Set<AccessFlag> flags) { 814 Set<String> s = new LinkedHashSet<>(); 815 for (var f : flags) 816 if (f.sourceModifier()) s.add(Modifier.toString(f.mask())); 817 return s; 818 } 819 820 private Set<String> getClassFlags(AccessFlags flags) { 821 return getFlags(flags.flagsMask(), flagsReportUnknown(flags, cffv())); 822 } 823 824 private static Set<String> getFlags(int mask, Set<AccessFlag> flags) { 825 Set<String> s = new LinkedHashSet<>(); 826 for (var f: flags) { 827 s.add("ACC_" + f.name()); 828 mask = mask & ~f.mask(); 829 } 830 while (mask != 0) { 831 int bit = Integer.highestOneBit(mask); 832 s.add("0x" + Integer.toHexString(bit)); 833 mask = mask & ~bit; 834 } 835 return s; 836 } 837 838 private final Options options; 839 private final AttributeWriter attrWriter; 840 private final CodeWriter codeWriter; 841 private final ConstantWriter constantWriter; 842 private ClassModel classModel; 843 private URI uri; 844 private long lastModified; 845 private String digestName; 846 private byte[] digest; 847 private int size; 848 private MethodModel method; 849 }