1 /* 2 * Copyright (c) 2007, 2024, 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.reflect.AccessFlag; 29 import java.net.URI; 30 import java.text.DateFormat; 31 import java.util.Collection; 32 import java.util.Date; 33 import java.util.EnumSet; 34 import java.util.List; 35 import java.util.Set; 36 37 import java.lang.constant.ClassDesc; 38 import java.lang.reflect.Modifier; 39 import java.util.ArrayList; 40 import java.util.LinkedHashSet; 41 42 import com.sun.tools.javac.code.Source; 43 import java.lang.classfile.AccessFlags; 44 import java.lang.classfile.Attributes; 45 import java.lang.classfile.ClassModel; 46 import java.lang.classfile.ClassSignature; 47 import java.lang.classfile.ClassFile; 48 import static java.lang.classfile.ClassFile.*; 49 import java.lang.classfile.ClassHierarchyResolver; 50 import java.lang.classfile.constantpool.*; 51 import java.lang.classfile.FieldModel; 52 import java.lang.classfile.MethodModel; 53 import java.lang.classfile.MethodSignature; 54 import java.lang.classfile.Signature; 55 import java.lang.classfile.attribute.CodeAttribute; 56 import java.lang.classfile.attribute.SignatureAttribute; 57 58 /* 59 * The main javap class to write the contents of a class file as text. 60 * 61 * <p><b>This is NOT part of any supported API. 62 * If you write code that depends on this, you do so at your own risk. 63 * This code and its internal interfaces are subject to change or 64 * deletion without notice.</b> 65 */ 66 public class ClassWriter extends BasicWriter { 67 static ClassWriter instance(Context context) { 68 ClassWriter instance = context.get(ClassWriter.class); 69 if (instance == null) 70 instance = new ClassWriter(context); 71 return instance; 72 } 73 74 protected ClassWriter(Context context) { 75 super(context); 76 context.put(ClassWriter.class, this); 77 options = Options.instance(context); 78 attrWriter = AttributeWriter.instance(context); 79 codeWriter = CodeWriter.instance(context); 80 constantWriter = ConstantWriter.instance(context); 81 sigPrinter = new SignaturePrinter(options.verbose); 82 } 83 84 void setDigest(String name, byte[] digest) { 85 this.digestName = name; 86 this.digest = digest; 87 } 88 89 void setFile(URI uri) { 90 this.uri = uri; 91 } 92 93 void setFileSize(int size) { 94 this.size = size; 95 } 96 97 void setLastModified(long lastModified) { 98 this.lastModified = lastModified; 99 } 100 101 protected ClassModel getClassModel() { 102 return classModel; 103 } 104 105 protected void setClassFile(ClassModel cm) { 106 classModel = cm; 107 } 108 109 protected MethodModel getMethod() { 110 return method; 111 } 112 113 protected void setMethod(MethodModel m) { 114 method = m; 115 } 116 117 public boolean write(ClassModel cm) { 118 errorReported = false; 119 setClassFile(cm); 120 121 if (options.sysInfo || options.verbose) { 122 if (uri != null) { 123 if (uri.getScheme().equals("file")) 124 println("Classfile " + uri.getPath()); 125 else 126 println("Classfile " + uri); 127 } 128 indent(+1); 129 if (lastModified != -1) { 130 Date lm = new Date(lastModified); 131 DateFormat df = DateFormat.getDateInstance(); 132 if (size > 0) { 133 println("Last modified " + df.format(lm) + "; size " + size 134 + " bytes"); 135 } else { 136 println("Last modified " + df.format(lm)); 137 } 138 } else if (size > 0) { 139 println("Size " + size + " bytes"); 140 } 141 if (digestName != null && digest != null) { 142 StringBuilder sb = new StringBuilder(); 143 for (byte b: digest) 144 sb.append(String.format("%02x", b)); 145 println(digestName + " checksum " + sb); 146 } 147 } 148 149 cm.findAttribute(Attributes.sourceFile()).ifPresent(sfa -> 150 println("Compiled from \"" + sfa.sourceFile().stringValue() + "\"")); 151 152 if (options.sysInfo || options.verbose) { 153 indent(-1); 154 } 155 156 writeModifiers(getClassModifiers(cm.flags(), classModel.majorVersion(), classModel.minorVersion())); 157 158 if ((classModel.flags().flagsMask() & ACC_MODULE) != 0) { 159 var attr = classModel.findAttribute(Attributes.module()); 160 if (attr.isPresent()) { 161 var modAttr = attr.get(); 162 if ((modAttr.moduleFlagsMask() & ACC_OPEN) != 0) { 163 print("open "); 164 } 165 print("module "); 166 print(() -> modAttr.moduleName().name().stringValue()); 167 if (modAttr.moduleVersion().isPresent()) { 168 print("@"); 169 print(() -> modAttr.moduleVersion().get().stringValue()); 170 } 171 } else { 172 // fallback for malformed class files 173 print("class "); 174 print(() -> getJavaName(classModel.thisClass().asInternalName())); 175 } 176 } else { 177 if ((classModel.flags().flagsMask() & ACC_INTERFACE) == 0) 178 print("class "); 179 else 180 print("interface "); 181 182 print(() -> getJavaName(classModel.thisClass().asInternalName())); 183 } 184 185 try { 186 var sigAttr = classModel.findAttribute(Attributes.signature()).orElse(null); 187 if (sigAttr == null) { 188 // use info from class file header 189 if ((classModel.flags().flagsMask() & ACC_INTERFACE) == 0 190 && classModel.superclass().isPresent()) { 191 String sn = getJavaName(classModel.superclass().get().asInternalName()); 192 if (!sn.equals("java.lang.Object")) { 193 print(" extends "); 194 print(sn); 195 } 196 } 197 var interfaces = classModel.interfaces(); 198 for (int i = 0; i < interfaces.size(); i++) { 199 print(i == 0 ? ((classModel.flags().flagsMask() & ACC_INTERFACE) == 0 200 ? " implements " : " extends ") : ","); 201 print(getJavaName(interfaces.get(i).asInternalName())); 202 } 203 } else { 204 var t = sigAttr.asClassSignature(); 205 print(sigPrinter.print(t, (classModel.flags().flagsMask() & ACC_INTERFACE) != 0)); 206 } 207 } catch (IllegalArgumentException e) { 208 report(e); 209 } 210 211 if (options.verbose) { 212 println(); 213 indent(+1); 214 println("minor version: " + classModel.minorVersion()); 215 println("major version: " + classModel.majorVersion()); 216 writeList(String.format("flags: (0x%04x) ", cm.flags().flagsMask()), 217 getClassFlags(cm.flags()), "\n"); 218 print("this_class: #");print(() -> classModel.thisClass().index()); 219 tab(); 220 print(() -> "// " + classModel.thisClass().asInternalName()); 221 println(); 222 print("super_class: #");print(() -> classModel.superclass() 223 .map(ClassEntry::index).orElse(0)); 224 try { 225 if (classModel.superclass().isPresent()) { 226 tab(); 227 print(() -> "// " + classModel.superclass().get().asInternalName()); 228 } 229 } catch (IllegalArgumentException e) { 230 report(e); 231 } 232 println(); 233 print("interfaces: ");print(() -> classModel.interfaces().size()); 234 print(", fields: " + classModel.fields().size()); 235 print(", methods: " + classModel.methods().size()); 236 println(", attributes: " + classModel.attributes().size()); 237 indent(-1); 238 constantWriter.writeConstantPool(); 239 } else { 240 print(" "); 241 } 242 243 println("{"); 244 indent(+1); 245 if ((cm.flags().flagsMask() & ACC_MODULE) != 0 && !options.verbose) { 246 writeDirectives(); 247 } 248 writeFields(); 249 writeMethods(); 250 indent(-1); 251 println("}"); 252 253 if (options.verbose) { 254 attrWriter.write(classModel.attributes()); 255 } 256 257 if (options.verify) { 258 var vErrors = VERIFIER.verify(classModel); 259 if (!vErrors.isEmpty()) { 260 println(); 261 for (var ve : vErrors) { 262 println(ve.getMessage()); 263 } 264 errorReported = true; 265 } 266 } 267 return !errorReported; 268 } 269 // where 270 271 private static final ClassFile VERIFIER = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of( 272 ClassHierarchyResolver.defaultResolver().orElse(new ClassHierarchyResolver() { 273 @Override 274 public ClassHierarchyResolver.ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { 275 // mark all unresolved classes as interfaces to exclude them from assignability verification 276 return ClassHierarchyInfo.ofInterface(); 277 } 278 }))); 279 280 final SignaturePrinter sigPrinter; 281 282 public static record SignaturePrinter(boolean verbose) { 283 284 public String print(ClassSignature cs, boolean isInterface) { 285 var sb = new StringBuilder(); 286 print(sb, cs.typeParameters()); 287 if (isInterface) { 288 String sep = " extends "; 289 for (var is : cs.superinterfaceSignatures()) { 290 sb.append(sep); 291 print(sb, is); 292 sep = ", "; 293 } 294 } else { 295 if (cs.superclassSignature() != null 296 && (verbose || !isObject(cs.superclassSignature()))) { 297 sb.append(" extends "); 298 print(sb, cs.superclassSignature()); 299 } 300 String sep = " implements "; 301 for (var is : cs.superinterfaceSignatures()) { 302 sb.append(sep); 303 print(sb, is); 304 sep = ", "; 305 } 306 } 307 return sb.toString(); 308 } 309 310 public String print(Signature sig) { 311 var sb = new StringBuilder(); 312 print(sb, sig); 313 return sb.toString(); 314 } 315 316 public String printTypeParams(List<Signature.TypeParam> tps) { 317 var sb = new StringBuilder(); 318 print(sb, tps); 319 return sb.toString(); 320 } 321 322 public String printList(String prefix, List<? extends Signature> args, 323 String postfix) { 324 var sb = new StringBuilder(); 325 sb.append(prefix); 326 String sep = ""; 327 for (var arg : args) { 328 sb.append(sep); 329 print(sb, arg); 330 sep = ", "; 331 } 332 return sb.append(postfix).toString(); 333 } 334 335 private boolean isObject(Signature sig) { 336 return (sig instanceof Signature.ClassTypeSig cts) 337 && cts.outerType().isEmpty() 338 && cts.className().equals("java/lang/Object") 339 && (cts.typeArgs().isEmpty()); 340 } 341 342 private void print(StringBuilder sb, List<Signature.TypeParam> tps) { 343 if (!tps.isEmpty()) { 344 sb.append('<'); 345 String sep = ""; 346 for (var tp : tps) { 347 sb.append(sep).append(tp.identifier()); 348 sep = " extends "; 349 if (tp.classBound().isPresent() 350 && (verbose || !isObject(tp.classBound().get()))) { 351 sb.append(sep); 352 print(sb, tp.classBound().get()); 353 sep = " & "; 354 } 355 for (var bound: tp.interfaceBounds()) { 356 sb.append(sep); 357 print(sb, bound); 358 sep = " & "; 359 } 360 sep = ", "; 361 } 362 sb.append('>'); 363 } 364 } 365 366 private void print(StringBuilder sb, Signature sig) { 367 if (sig instanceof Signature.BaseTypeSig bts) { 368 sb.append(ClassDesc.ofDescriptor("" + bts.baseType()).displayName()); 369 } else if (sig instanceof Signature.ClassTypeSig cts) { 370 if (cts.outerType().isPresent()) { 371 print(sb, cts.outerType().get()); 372 sb.append("."); 373 } 374 sb.append(getJavaName(cts.className())); 375 if (!cts.typeArgs().isEmpty()) { 376 String sep = ""; 377 sb.append('<'); 378 for (var ta : cts.typeArgs()) { 379 sb.append(sep); 380 print(sb, ta); 381 sep = ", "; 382 } 383 sb.append('>'); 384 } 385 } else if (sig instanceof Signature.TypeVarSig tvs) { 386 sb.append(tvs.identifier()); 387 } else if (sig instanceof Signature.ArrayTypeSig ats) { 388 print(sb, ats.componentSignature()); 389 sb.append("[]"); 390 } 391 } 392 393 private void print(StringBuilder sb, Signature.TypeArg ta) { 394 switch (ta) { 395 case Signature.TypeArg.Unbounded _ -> sb.append('?'); 396 case Signature.TypeArg.Bounded bta -> { 397 switch (bta.wildcardIndicator()) { 398 case NONE -> print(sb, bta.boundType()); 399 case EXTENDS -> { 400 sb.append("? extends "); 401 print(sb, bta.boundType()); 402 } 403 case SUPER -> { 404 sb.append("? super "); 405 print(sb, bta.boundType()); 406 } 407 } 408 } 409 } 410 } 411 } 412 413 protected void writeFields() { 414 for (var f: classModel.fields()) { 415 writeField(f); 416 } 417 } 418 419 protected void writeField(FieldModel f) { 420 if (!options.checkAccess(f.flags().flagsMask())) 421 return; 422 423 var flags = f.flags(); 424 writeModifiers(flagsReportUnknown(flags).stream().filter(fl -> fl.sourceModifier()) 425 .map(fl -> Modifier.toString(fl.mask())).toList()); 426 print(() -> sigPrinter.print( 427 f.findAttribute(Attributes.signature()) 428 .map(SignatureAttribute::asTypeSignature) 429 .orElseGet(() -> Signature.of(f.fieldTypeSymbol())))); 430 print(" "); 431 print(() -> f.fieldName().stringValue()); 432 if (options.showConstants) { 433 var a = f.findAttribute(Attributes.constantValue()); 434 if (a.isPresent()) { 435 print(" = "); 436 var cv = a.get(); 437 print(() -> getConstantValue(f.fieldTypeSymbol(), cv.constant())); 438 } 439 } 440 print(";"); 441 println(); 442 443 indent(+1); 444 445 boolean showBlank = false; 446 447 if (options.showDescriptors) { 448 print("descriptor: ");println(() -> f.fieldType().stringValue()); 449 } 450 451 if (options.verbose) 452 writeList(String.format("flags: (0x%04x) ", flags.flagsMask()), 453 flagsReportUnknown(flags).stream().map(fl -> "ACC_" + fl.toString()).toList(), 454 "\n"); 455 456 if (options.showAllAttrs) { 457 attrWriter.write(f.attributes()); 458 showBlank = true; 459 } 460 461 indent(-1); 462 463 if (showBlank || options.showDisassembled || options.showLineAndLocalVariableTables) 464 println(); 465 } 466 467 protected void writeMethods() { 468 for (MethodModel m: classModel.methods()) 469 writeMethod(m); 470 setPendingNewline(false); 471 } 472 473 private static final int DEFAULT_ALLOWED_MAJOR_VERSION = 52; 474 private static final int DEFAULT_ALLOWED_MINOR_VERSION = 0; 475 476 protected void writeMethod(MethodModel m) { 477 if (!options.checkAccess(m.flags().flagsMask())) 478 return; 479 480 method = m; 481 482 int flags = m.flags().flagsMask(); 483 484 var modifiers = new ArrayList<String>(); 485 for (var f : flagsReportUnknown(m.flags())) 486 if (f.sourceModifier()) modifiers.add(Modifier.toString(f.mask())); 487 488 String name = "???"; 489 try { 490 name = m.methodName().stringValue(); 491 } catch (IllegalArgumentException e) { 492 report(e); 493 } 494 495 if ((classModel.flags().flagsMask() & ACC_INTERFACE) != 0 && 496 ((flags & ACC_ABSTRACT) == 0) && !name.equals("<clinit>")) { 497 if (classModel.majorVersion() > DEFAULT_ALLOWED_MAJOR_VERSION || 498 (classModel.majorVersion() == DEFAULT_ALLOWED_MAJOR_VERSION 499 && classModel.minorVersion() >= DEFAULT_ALLOWED_MINOR_VERSION)) { 500 if ((flags & (ACC_STATIC | ACC_PRIVATE)) == 0) { 501 modifiers.add("default"); 502 } 503 } 504 } 505 writeModifiers(modifiers); 506 507 try { 508 var sigAttr = m.findAttribute(Attributes.signature()); 509 MethodSignature d; 510 if (sigAttr.isEmpty()) { 511 d = MethodSignature.parseFrom(m.methodType().stringValue()); 512 } else { 513 d = sigAttr.get().asMethodSignature(); 514 } 515 516 if (!d.typeParameters().isEmpty()) { 517 print(sigPrinter.printTypeParams(d.typeParameters()) + " "); 518 } 519 switch (name) { 520 case "<init>": 521 print(getJavaName(classModel.thisClass().asInternalName())); 522 print(getJavaParameterTypes(d, flags)); 523 break; 524 case "<clinit>": 525 print("{}"); 526 break; 527 default: 528 print(getJavaName(sigPrinter.print(d.result()))); 529 print(" "); 530 print(name); 531 print(getJavaParameterTypes(d, flags)); 532 break; 533 } 534 535 var e_attr = m.findAttribute(Attributes.exceptions()); 536 // if there are generic exceptions, there must be erased exceptions 537 if (e_attr.isPresent()) { 538 var exceptions = e_attr.get(); 539 print(" throws "); 540 if (d != null && !d.throwableSignatures().isEmpty()) { // use generic list if available 541 print(() -> sigPrinter.printList("", d.throwableSignatures(), "")); 542 } else { 543 var exNames = exceptions.exceptions(); 544 for (int i = 0; i < exNames.size(); i++) { 545 if (i > 0) 546 print(", "); 547 int ii = i; 548 print(() -> getJavaName(exNames.get(ii).asInternalName())); 549 } 550 } 551 } 552 } catch (IllegalArgumentException e) { 553 report(e); 554 } 555 556 println(";"); 557 558 indent(+1); 559 560 if (options.showDescriptors) { 561 print("descriptor: ");println(() -> m.methodType().stringValue()); 562 } 563 564 if (options.verbose) { 565 StringBuilder sb = new StringBuilder(); 566 String sep = ""; 567 sb.append(String.format("flags: (0x%04x) ", flags)); 568 for (var f : flagsReportUnknown(m.flags())) { 569 sb.append(sep).append("ACC_").append(f.name()); 570 sep = ", "; 571 } 572 println(sb.toString()); 573 } 574 575 var code = (CodeAttribute)m.code().orElse(null); 576 577 if (options.showAllAttrs) { 578 attrWriter.write(m.attributes()); 579 } else if (code != null) { 580 if (options.showDisassembled) { 581 println("Code:"); 582 codeWriter.writeInstrs(code); 583 codeWriter.writeExceptionTable(code); 584 } 585 586 if (options.showLineAndLocalVariableTables) { 587 code.findAttribute(Attributes.lineNumberTable()) 588 .ifPresent(a -> attrWriter.write(a, code)); 589 code.findAttribute(Attributes.localVariableTable()) 590 .ifPresent(a -> attrWriter.write(a, code)); 591 } 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); 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> getClassModifiers(AccessFlags flags, int majorVersion, int minorVersion) { 814 boolean previewClassFile = minorVersion == ClassFile.PREVIEW_MINOR_VERSION; 815 Set<AccessFlag> flagSet = flags.flags(); 816 if (flagSet.contains(AccessFlag.INTERFACE)) { 817 flagSet = EnumSet.copyOf(flagSet); 818 flagSet.remove(AccessFlag.ABSTRACT); 819 } else if (Source.isSupported(Source.Feature.VALUE_CLASSES, majorVersion) && previewClassFile) { 820 Set<String> classModifers = getModifiers(flagSet); 821 classModifers.add("value"); 822 return classModifers; 823 } 824 return getModifiers(flagSet); 825 } 826 827 private static Set<String> getModifiers(Set<java.lang.reflect.AccessFlag> flags) { 828 Set<String> s = new LinkedHashSet<>(); 829 for (var f : flags) 830 if (f.sourceModifier()) s.add(Modifier.toString(f.mask())); 831 return s; 832 } 833 834 private Set<String> getClassFlags(AccessFlags flags) { 835 return getFlags(flags.flagsMask(), flagsReportUnknown(flags)); 836 } 837 838 private static Set<String> getFlags(int mask, Set<java.lang.reflect.AccessFlag> flags) { 839 Set<String> s = new LinkedHashSet<>(); 840 for (var f: flags) { 841 s.add("ACC_" + f.name()); 842 mask = mask & ~f.mask(); 843 } 844 while (mask != 0) { 845 int bit = Integer.highestOneBit(mask); 846 s.add("0x" + Integer.toHexString(bit)); 847 mask = mask & ~bit; 848 } 849 return s; 850 } 851 852 private final Options options; 853 private final AttributeWriter attrWriter; 854 private final CodeWriter codeWriter; 855 private final ConstantWriter constantWriter; 856 private ClassModel classModel; 857 private URI uri; 858 private long lastModified; 859 private String digestName; 860 private byte[] digest; 861 private int size; 862 private MethodModel method; 863 }