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.name()).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 codeWriter.writeMinimal(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 Set<String> getClassModifiers(AccessFlags flags, int majorVersion, int minorVersion) { 812 boolean previewClassFile = minorVersion == ClassFile.PREVIEW_MINOR_VERSION; 813 Set<AccessFlag> flagSet = flagsReportUnknown(flags); 814 if (flagSet.contains(AccessFlag.INTERFACE)) { 815 flagSet = EnumSet.copyOf(flagSet); 816 flagSet.remove(AccessFlag.ABSTRACT); 817 } else if (Source.isSupported(Source.Feature.VALUE_CLASSES, majorVersion) && previewClassFile) { 818 Set<String> classModifers = getModifiers(flagSet); 819 classModifers.add("value"); 820 return classModifers; 821 } 822 return getModifiers(flagSet); 823 } 824 825 private static Set<String> getModifiers(Set<java.lang.reflect.AccessFlag> flags) { 826 Set<String> s = new LinkedHashSet<>(); 827 for (var f : flags) 828 if (f.sourceModifier()) s.add(Modifier.toString(f.mask())); 829 return s; 830 } 831 832 private Set<String> getClassFlags(AccessFlags flags) { 833 return getFlags(flags.flagsMask(), flagsReportUnknown(flags)); 834 } 835 836 private static Set<String> getFlags(int mask, Set<java.lang.reflect.AccessFlag> flags) { 837 Set<String> s = new LinkedHashSet<>(); 838 for (var f: flags) { 839 s.add("ACC_" + f.name()); 840 mask = mask & ~f.mask(); 841 } 842 while (mask != 0) { 843 int bit = Integer.highestOneBit(mask); 844 s.add("0x" + Integer.toHexString(bit)); 845 mask = mask & ~bit; 846 } 847 return s; 848 } 849 850 private final Options options; 851 private final AttributeWriter attrWriter; 852 private final CodeWriter codeWriter; 853 private final ConstantWriter constantWriter; 854 private ClassModel classModel; 855 private URI uri; 856 private long lastModified; 857 private String digestName; 858 private byte[] digest; 859 private int size; 860 private MethodModel method; 861 }