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