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