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