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