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