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 com.sun.tools.classfile.AccessFlags; 36 import com.sun.tools.classfile.Attribute; 37 import com.sun.tools.classfile.Attributes; 38 import com.sun.tools.classfile.ClassFile; 39 import com.sun.tools.classfile.Code_attribute; 40 import com.sun.tools.classfile.ConstantPool; 41 import com.sun.tools.classfile.ConstantPoolException; 42 import com.sun.tools.classfile.ConstantValue_attribute; 43 import com.sun.tools.classfile.Descriptor; 44 import com.sun.tools.classfile.Descriptor.InvalidDescriptor; 45 import com.sun.tools.classfile.Exceptions_attribute; 46 import com.sun.tools.classfile.Field; 47 import com.sun.tools.classfile.Method; 48 import com.sun.tools.classfile.Module_attribute; 49 import com.sun.tools.classfile.Signature; 50 import com.sun.tools.classfile.Signature_attribute; 51 import com.sun.tools.classfile.SourceFile_attribute; 52 import com.sun.tools.classfile.Type; 53 import com.sun.tools.classfile.Type.ArrayType; 54 import com.sun.tools.classfile.Type.ClassSigType; 55 import com.sun.tools.classfile.Type.ClassType; 56 import com.sun.tools.classfile.Type.MethodType; 57 import com.sun.tools.classfile.Type.SimpleType; 58 import com.sun.tools.classfile.Type.TypeParamType; 59 import com.sun.tools.classfile.Type.WildcardType; 60 61 import static com.sun.tools.classfile.AccessFlags.*; 62 import static com.sun.tools.classfile.ConstantPool.CONSTANT_Module; 63 import static com.sun.tools.classfile.ConstantPool.CONSTANT_Package; 64 65 /* 66 * The main javap class to write the contents of a class file as text. 67 * 68 * <p><b>This is NOT part of any supported API. 69 * If you write code that depends on this, you do so at your own risk. 70 * This code and its internal interfaces are subject to change or 71 * deletion without notice.</b> 72 */ 73 public class ClassWriter extends BasicWriter { 74 static ClassWriter instance(Context context) { 75 ClassWriter instance = context.get(ClassWriter.class); 76 if (instance == null) 77 instance = new ClassWriter(context); 78 return instance; 79 } 80 81 protected ClassWriter(Context context) { 82 super(context); 83 context.put(ClassWriter.class, this); 84 options = Options.instance(context); 85 attrWriter = AttributeWriter.instance(context); 86 codeWriter = CodeWriter.instance(context); 87 constantWriter = ConstantWriter.instance(context); 88 } 89 90 void setDigest(String name, byte[] digest) { 91 this.digestName = name; 92 this.digest = digest; 93 } 94 95 void setFile(URI uri) { 96 this.uri = uri; 97 } 98 99 void setFileSize(int size) { 100 this.size = size; 101 } 102 103 void setLastModified(long lastModified) { 104 this.lastModified = lastModified; 105 } 106 107 protected ClassFile getClassFile() { 108 return classFile; 109 } 110 111 protected void setClassFile(ClassFile cf) { 112 classFile = cf; 113 constant_pool = classFile.constant_pool; 114 } 115 116 protected Method getMethod() { 117 return method; 118 } 119 120 protected void setMethod(Method m) { 121 method = m; 122 } 123 124 public void write(ClassFile cf) { 125 setClassFile(cf); 126 127 if (options.sysInfo || options.verbose) { 128 if (uri != null) { 129 if (uri.getScheme().equals("file")) 130 println("Classfile " + uri.getPath()); 131 else 132 println("Classfile " + uri); 133 } 134 indent(+1); 135 if (lastModified != -1) { 136 Date lm = new Date(lastModified); 137 DateFormat df = DateFormat.getDateInstance(); 138 if (size > 0) { 139 println("Last modified " + df.format(lm) + "; size " + size + " bytes"); 140 } else { 141 println("Last modified " + df.format(lm)); 142 } 143 } else if (size > 0) { 144 println("Size " + size + " bytes"); 145 } 146 if (digestName != null && digest != null) { 147 StringBuilder sb = new StringBuilder(); 148 for (byte b: digest) 149 sb.append(String.format("%02x", b)); 150 println(digestName + " checksum " + sb); 151 } 152 } 153 154 Attribute sfa = cf.getAttribute(Attribute.SourceFile); 155 if (sfa instanceof SourceFile_attribute) { 156 println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\""); 157 } 158 159 if (options.sysInfo || options.verbose) { 160 indent(-1); 161 } 162 163 AccessFlags flags = cf.access_flags; 164 writeModifiers(flags.getClassModifiers()); 165 166 if (classFile.access_flags.is(AccessFlags.ACC_MODULE)) { 167 Attribute attr = classFile.attributes.get(Attribute.Module); 168 if (attr instanceof Module_attribute) { 169 Module_attribute modAttr = (Module_attribute) attr; 170 String name; 171 try { 172 // FIXME: compatibility code 173 if (constant_pool.get(modAttr.module_name).getTag() == CONSTANT_Module) { 174 name = getJavaName(constant_pool.getModuleInfo(modAttr.module_name).getName()); 175 } else { 176 name = getJavaName(constant_pool.getUTF8Value(modAttr.module_name)); 177 } 178 } catch (ConstantPoolException e) { 179 name = report(e); 180 } 181 if ((modAttr.module_flags & Module_attribute.ACC_OPEN) != 0) { 182 print("open "); 183 } 184 print("module "); 185 print(name); 186 if (modAttr.module_version_index != 0) { 187 print("@"); 188 print(getUTF8Value(modAttr.module_version_index)); 189 } 190 } else { 191 // fallback for malformed class files 192 print("class "); 193 print(getJavaName(classFile)); 194 } 195 } else { 196 if (classFile.isClass()) 197 print("class "); 198 else if (classFile.isInterface()) 199 print("interface "); 200 201 print(getJavaName(classFile)); 202 } 203 204 Signature_attribute sigAttr = getSignature(cf.attributes); 205 if (sigAttr == null) { 206 // use info from class file header 207 if (classFile.isClass() && classFile.super_class != 0 ) { 208 String sn = getJavaSuperclassName(cf); 209 if (!sn.equals("java.lang.Object")) { 210 print(" extends "); 211 print(sn); 212 } 213 } 214 for (int i = 0; i < classFile.interfaces.length; i++) { 215 print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ","); 216 print(getJavaInterfaceName(classFile, i)); 217 } 218 } else { 219 try { 220 Type t = sigAttr.getParsedSignature().getType(constant_pool); 221 JavaTypePrinter p = new JavaTypePrinter(classFile.isInterface()); 222 // The signature parser cannot disambiguate between a 223 // FieldType and a ClassSignatureType that only contains a superclass type. 224 if (t instanceof Type.ClassSigType) { 225 print(p.print(t)); 226 } else if (options.verbose || !t.isObject()) { 227 print(" extends "); 228 print(p.print(t)); 229 } 230 } catch (ConstantPoolException e) { 231 print(report(e)); 232 } catch (IllegalStateException e) { 233 report("Invalid value for Signature attribute: " + e.getMessage()); 234 } 235 } 236 237 if (options.verbose) { 238 println(); 239 indent(+1); 240 println("minor version: " + cf.minor_version); 241 println("major version: " + cf.major_version); 242 writeList(String.format("flags: (0x%04x) ", flags.flags), flags.getClassFlags(), "\n"); 243 print("this_class: #" + cf.this_class); 244 if (cf.this_class != 0) { 245 tab(); 246 print("// " + constantWriter.stringValue(cf.this_class)); 247 } 248 println(); 249 print("super_class: #" + cf.super_class); 250 if (cf.super_class != 0) { 251 tab(); 252 print("// " + constantWriter.stringValue(cf.super_class)); 253 } 254 println(); 255 print("interfaces: " + cf.interfaces.length); 256 print(", fields: " + cf.fields.length); 257 print(", methods: " + cf.methods.length); 258 println(", attributes: " + cf.attributes.attrs.length); 259 indent(-1); 260 constantWriter.writeConstantPool(); 261 } else { 262 print(" "); 263 } 264 265 println("{"); 266 indent(+1); 267 if (flags.is(AccessFlags.ACC_MODULE) && !options.verbose) { 268 writeDirectives(); 269 } 270 writeFields(); 271 writeMethods(); 272 indent(-1); 273 println("}"); 274 275 if (options.verbose) { 276 attrWriter.write(cf, cf.attributes, constant_pool); 277 } 278 } 279 // where 280 class JavaTypePrinter implements Type.Visitor<StringBuilder,StringBuilder> { 281 boolean isInterface; 282 283 JavaTypePrinter(boolean isInterface) { 284 this.isInterface = isInterface; 285 } 286 287 String print(Type t) { 288 return t.accept(this, new StringBuilder()).toString(); 289 } 290 291 String printTypeArgs(List<? extends TypeParamType> typeParamTypes) { 292 StringBuilder builder = new StringBuilder(); 293 appendIfNotEmpty(builder, "<", typeParamTypes, "> "); 294 return builder.toString(); 295 } 296 297 @Override 298 public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) { 299 sb.append(getJavaName(type.name)); 300 return sb; 301 } 302 303 @Override 304 public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) { 305 append(sb, type.elemType); 306 sb.append("[]"); 307 return sb; 308 } 309 310 @Override 311 public StringBuilder visitMethodType(MethodType type, StringBuilder sb) { 312 appendIfNotEmpty(sb, "<", type.typeParamTypes, "> "); 313 append(sb, type.returnType); 314 append(sb, " (", type.paramTypes, ")"); 315 appendIfNotEmpty(sb, " throws ", type.throwsTypes, ""); 316 return sb; 317 } 318 319 @Override 320 public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) { 321 appendIfNotEmpty(sb, "<", type.typeParamTypes, ">"); 322 if (isInterface) { 323 appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, ""); 324 } else { 325 if (type.superclassType != null 326 && (options.verbose || !type.superclassType.isObject())) { 327 sb.append(" extends "); 328 append(sb, type.superclassType); 329 } 330 appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, ""); 331 } 332 return sb; 333 } 334 335 @Override 336 public StringBuilder visitClassType(ClassType type, StringBuilder sb) { 337 if (type.outerType != null) { 338 append(sb, type.outerType); 339 sb.append("."); 340 } 341 sb.append(getJavaName(type.name)); 342 appendIfNotEmpty(sb, "<", type.typeArgs, ">"); 343 return sb; 344 } 345 346 @Override 347 public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) { 348 sb.append(type.name); 349 String sep = " extends "; 350 if (type.classBound != null 351 && (options.verbose || !type.classBound.isObject())) { 352 sb.append(sep); 353 append(sb, type.classBound); 354 sep = " & "; 355 } 356 if (type.interfaceBounds != null) { 357 for (Type bound: type.interfaceBounds) { 358 sb.append(sep); 359 append(sb, bound); 360 sep = " & "; 361 } 362 } 363 return sb; 364 } 365 366 @Override 367 public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) { 368 switch (type.kind) { 369 case UNBOUNDED: 370 sb.append("?"); 371 break; 372 case EXTENDS: 373 sb.append("? extends "); 374 append(sb, type.boundType); 375 break; 376 case SUPER: 377 sb.append("? super "); 378 append(sb, type.boundType); 379 break; 380 default: 381 throw new AssertionError(); 382 } 383 return sb; 384 } 385 386 private void append(StringBuilder sb, Type t) { 387 t.accept(this, sb); 388 } 389 390 private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) { 391 sb.append(prefix); 392 String sep = ""; 393 for (Type t: list) { 394 sb.append(sep); 395 append(sb, t); 396 sep = ", "; 397 } 398 sb.append(suffix); 399 } 400 401 private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) { 402 if (!isEmpty(list)) 403 append(sb, prefix, list, suffix); 404 } 405 406 private boolean isEmpty(List<? extends Type> list) { 407 return (list == null || list.isEmpty()); 408 } 409 } 410 411 protected void writeFields() { 412 for (Field f: classFile.fields) { 413 writeField(f); 414 } 415 } 416 417 protected void writeField(Field f) { 418 if (!options.checkAccess(f.access_flags)) 419 return; 420 421 AccessFlags flags = f.access_flags; 422 writeModifiers(flags.getFieldModifiers()); 423 Signature_attribute sigAttr = getSignature(f.attributes); 424 if (sigAttr == null) 425 print(getJavaFieldType(f.descriptor)); 426 else { 427 try { 428 Type t = sigAttr.getParsedSignature().getType(constant_pool); 429 print(getJavaName(t.toString())); 430 } catch (ConstantPoolException e) { 431 // report error? 432 // fall back on non-generic descriptor 433 print(getJavaFieldType(f.descriptor)); 434 } 435 } 436 print(" "); 437 print(getFieldName(f)); 438 if (options.showConstants) { 439 Attribute a = f.attributes.get(Attribute.ConstantValue); 440 if (a instanceof ConstantValue_attribute) { 441 print(" = "); 442 ConstantValue_attribute cv = (ConstantValue_attribute) a; 443 print(getConstantValue(f.descriptor, cv.constantvalue_index)); 444 } 445 } 446 print(";"); 447 println(); 448 449 indent(+1); 450 451 boolean showBlank = false; 452 453 if (options.showDescriptors) 454 println("descriptor: " + getValue(f.descriptor)); 455 456 if (options.verbose) 457 writeList(String.format("flags: (0x%04x) ", flags.flags), flags.getFieldFlags(), "\n"); 458 459 if (options.showAllAttrs) { 460 for (Attribute attr: f.attributes) 461 attrWriter.write(f, attr, constant_pool); 462 showBlank = true; 463 } 464 465 indent(-1); 466 467 if (showBlank || options.showDisassembled || options.showLineAndLocalVariableTables) 468 println(); 469 } 470 471 protected void writeMethods() { 472 for (Method m: classFile.methods) 473 writeMethod(m); 474 setPendingNewline(false); 475 } 476 477 private static final int DEFAULT_ALLOWED_MAJOR_VERSION = 52; 478 private static final int DEFAULT_ALLOWED_MINOR_VERSION = 0; 479 480 protected void writeMethod(Method m) { 481 if (!options.checkAccess(m.access_flags)) 482 return; 483 484 method = m; 485 486 AccessFlags flags = m.access_flags; 487 488 Descriptor d; 489 Type.MethodType methodType; 490 List<? extends Type> methodExceptions; 491 492 Signature_attribute sigAttr = getSignature(m.attributes); 493 if (sigAttr == null) { 494 d = m.descriptor; 495 methodType = null; 496 methodExceptions = null; 497 } else { 498 Signature methodSig = sigAttr.getParsedSignature(); 499 d = methodSig; 500 try { 501 methodType = (Type.MethodType) methodSig.getType(constant_pool); 502 methodExceptions = methodType.throwsTypes; 503 if (methodExceptions != null && methodExceptions.isEmpty()) 504 methodExceptions = null; 505 } catch (ConstantPoolException | IllegalStateException e) { 506 // report error? 507 // fall back on standard descriptor 508 methodType = null; 509 methodExceptions = null; 510 } 511 } 512 513 Set<String> modifiers = flags.getMethodModifiers(); 514 515 String name = getName(m); 516 if (classFile.isInterface() && 517 (!flags.is(AccessFlags.ACC_ABSTRACT)) && !name.equals("<clinit>")) { 518 if (classFile.major_version > DEFAULT_ALLOWED_MAJOR_VERSION || 519 (classFile.major_version == DEFAULT_ALLOWED_MAJOR_VERSION && classFile.minor_version >= DEFAULT_ALLOWED_MINOR_VERSION)) { 520 if (!flags.is(AccessFlags.ACC_STATIC | AccessFlags.ACC_PRIVATE)) { 521 modifiers.add("default"); 522 } 523 } 524 } 525 526 writeModifiers(modifiers); 527 if (methodType != null) { 528 print(new JavaTypePrinter(false).printTypeArgs(methodType.typeParamTypes)); 529 } 530 switch (name) { 531 case "<init>": 532 String returnType = getJavaReturnType(d); 533 if (!returnType.equals("void")) { // static factories for primitive classes 534 print(returnType); 535 print(" "); 536 } 537 print(getJavaName(classFile)); 538 print(getJavaParameterTypes(d, flags)); 539 break; 540 case "<clinit>": 541 print("{}"); 542 break; 543 default: 544 print(getJavaReturnType(d)); 545 print(" "); 546 print(name); 547 print(getJavaParameterTypes(d, flags)); 548 break; 549 } 550 551 Attribute e_attr = m.attributes.get(Attribute.Exceptions); 552 if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions 553 if (e_attr instanceof Exceptions_attribute) { 554 Exceptions_attribute exceptions = (Exceptions_attribute) e_attr; 555 print(" throws "); 556 if (methodExceptions != null) { // use generic list if available 557 writeList("", methodExceptions, ""); 558 } else { 559 for (int i = 0; i < exceptions.number_of_exceptions; i++) { 560 if (i > 0) 561 print(", "); 562 print(getJavaException(exceptions, i)); 563 } 564 } 565 } else { 566 report("Unexpected or invalid value for Exceptions attribute"); 567 } 568 } 569 570 println(";"); 571 572 indent(+1); 573 574 if (options.showDescriptors) { 575 println("descriptor: " + getValue(m.descriptor)); 576 } 577 578 if (options.verbose) { 579 writeList(String.format("flags: (0x%04x) ", flags.flags), flags.getMethodFlags(), "\n"); 580 } 581 582 Code_attribute code = null; 583 Attribute c_attr = m.attributes.get(Attribute.Code); 584 if (c_attr != null) { 585 if (c_attr instanceof Code_attribute) 586 code = (Code_attribute) c_attr; 587 else 588 report("Unexpected or invalid value for Code attribute"); 589 } 590 591 if (options.showAllAttrs) { 592 Attribute[] attrs = m.attributes.attrs; 593 for (Attribute attr: attrs) 594 attrWriter.write(m, attr, constant_pool); 595 } else if (code != null) { 596 if (options.showDisassembled) { 597 println("Code:"); 598 codeWriter.writeInstrs(code); 599 codeWriter.writeExceptionTable(code); 600 } 601 602 if (options.showLineAndLocalVariableTables) { 603 attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool); 604 attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool); 605 } 606 } 607 608 indent(-1); 609 610 // set pendingNewline to write a newline before the next method (if any) 611 // if a separator is desired 612 setPendingNewline( 613 options.showDisassembled || 614 options.showAllAttrs || 615 options.showDescriptors || 616 options.showLineAndLocalVariableTables || 617 options.verbose); 618 } 619 620 void writeModifiers(Collection<String> items) { 621 for (Object item: items) { 622 print(item); 623 print(" "); 624 } 625 } 626 627 void writeDirectives() { 628 Attribute attr = classFile.attributes.get(Attribute.Module); 629 if (!(attr instanceof Module_attribute)) 630 return; 631 632 Module_attribute m = (Module_attribute) attr; 633 for (Module_attribute.RequiresEntry entry: m.requires) { 634 print("requires"); 635 if ((entry.requires_flags & Module_attribute.ACC_STATIC_PHASE) != 0) 636 print(" static"); 637 if ((entry.requires_flags & Module_attribute.ACC_TRANSITIVE) != 0) 638 print(" transitive"); 639 print(" "); 640 String mname; 641 try { 642 mname = getModuleName(entry.requires_index); 643 } catch (ConstantPoolException e) { 644 mname = report(e); 645 } 646 print(mname); 647 println(";"); 648 } 649 650 for (Module_attribute.ExportsEntry entry: m.exports) { 651 print("exports"); 652 print(" "); 653 String pname; 654 try { 655 pname = getPackageName(entry.exports_index).replace('/', '.'); 656 } catch (ConstantPoolException e) { 657 pname = report(e); 658 } 659 print(pname); 660 boolean first = true; 661 for (int i: entry.exports_to_index) { 662 String mname; 663 try { 664 mname = getModuleName(i); 665 } catch (ConstantPoolException e) { 666 mname = report(e); 667 } 668 if (first) { 669 println(" to"); 670 indent(+1); 671 first = false; 672 } else { 673 println(","); 674 } 675 print(mname); 676 } 677 println(";"); 678 if (!first) 679 indent(-1); 680 } 681 682 for (Module_attribute.OpensEntry entry: m.opens) { 683 print("opens"); 684 print(" "); 685 String pname; 686 try { 687 pname = getPackageName(entry.opens_index).replace('/', '.'); 688 } catch (ConstantPoolException e) { 689 pname = report(e); 690 } 691 print(pname); 692 boolean first = true; 693 for (int i: entry.opens_to_index) { 694 String mname; 695 try { 696 mname = getModuleName(i); 697 } catch (ConstantPoolException e) { 698 mname = report(e); 699 } 700 if (first) { 701 println(" to"); 702 indent(+1); 703 first = false; 704 } else { 705 println(","); 706 } 707 print(mname); 708 } 709 println(";"); 710 if (!first) 711 indent(-1); 712 } 713 714 for (int entry: m.uses_index) { 715 print("uses "); 716 print(getClassName(entry).replace('/', '.')); 717 println(";"); 718 } 719 720 for (Module_attribute.ProvidesEntry entry: m.provides) { 721 print("provides "); 722 print(getClassName(entry.provides_index).replace('/', '.')); 723 boolean first = true; 724 for (int i: entry.with_index) { 725 if (first) { 726 println(" with"); 727 indent(+1); 728 first = false; 729 } else { 730 println(","); 731 } 732 print(getClassName(i).replace('/', '.')); 733 } 734 println(";"); 735 if (!first) 736 indent(-1); 737 } 738 } 739 740 String getModuleName(int index) throws ConstantPoolException { 741 if (constant_pool.get(index).getTag() == CONSTANT_Module) { 742 return constant_pool.getModuleInfo(index).getName(); 743 } else { 744 return constant_pool.getUTF8Value(index); 745 } 746 } 747 748 String getPackageName(int index) throws ConstantPoolException { 749 if (constant_pool.get(index).getTag() == CONSTANT_Package) { 750 return constant_pool.getPackageInfo(index).getName(); 751 } else { 752 return constant_pool.getUTF8Value(index); 753 } 754 } 755 756 String getUTF8Value(int index) { 757 try { 758 return classFile.constant_pool.getUTF8Value(index); 759 } catch (ConstantPoolException e) { 760 return report(e); 761 } 762 } 763 764 String getClassName(int index) { 765 try { 766 return classFile.constant_pool.getClassInfo(index).getName(); 767 } catch (ConstantPoolException e) { 768 return report(e); 769 } 770 } 771 772 void writeList(String prefix, Collection<?> items, String suffix) { 773 print(prefix); 774 String sep = ""; 775 for (Object item: items) { 776 print(sep); 777 print(item); 778 sep = ", "; 779 } 780 print(suffix); 781 } 782 783 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) { 784 if (items != null && items.size() > 0) 785 writeList(prefix, items, suffix); 786 } 787 788 Signature_attribute getSignature(Attributes attributes) { 789 return (Signature_attribute) attributes.get(Attribute.Signature); 790 } 791 792 String adjustVarargs(AccessFlags flags, String params) { 793 if (flags.is(ACC_VARARGS)) { 794 int i = params.lastIndexOf("[]"); 795 if (i > 0) 796 return params.substring(0, i) + "..." + params.substring(i+2); 797 } 798 799 return params; 800 } 801 802 String getJavaName(ClassFile cf) { 803 try { 804 return getJavaName(cf.getName()); 805 } catch (ConstantPoolException e) { 806 return report(e); 807 } 808 } 809 810 String getJavaSuperclassName(ClassFile cf) { 811 try { 812 return getJavaName(cf.getSuperclassName()); 813 } catch (ConstantPoolException e) { 814 return report(e); 815 } 816 } 817 818 String getJavaInterfaceName(ClassFile cf, int index) { 819 try { 820 return getJavaName(cf.getInterfaceName(index)); 821 } catch (ConstantPoolException e) { 822 return report(e); 823 } 824 } 825 826 String getJavaFieldType(Descriptor d) { 827 try { 828 return getJavaName(d.getFieldType(constant_pool)); 829 } catch (ConstantPoolException e) { 830 return report(e); 831 } catch (InvalidDescriptor e) { 832 return report(e); 833 } 834 } 835 836 String getJavaReturnType(Descriptor d) { 837 try { 838 return getJavaName(d.getReturnType(constant_pool)); 839 } catch (ConstantPoolException e) { 840 return report(e); 841 } catch (InvalidDescriptor e) { 842 return report(e); 843 } 844 } 845 846 String getJavaParameterTypes(Descriptor d, AccessFlags flags) { 847 try { 848 return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool))); 849 } catch (ConstantPoolException e) { 850 return report(e); 851 } catch (InvalidDescriptor e) { 852 return report(e); 853 } 854 } 855 856 String getJavaException(Exceptions_attribute attr, int index) { 857 try { 858 return getJavaName(attr.getException(index, constant_pool)); 859 } catch (ConstantPoolException e) { 860 return report(e); 861 } 862 } 863 864 String getValue(Descriptor d) { 865 try { 866 return d.getValue(constant_pool); 867 } catch (ConstantPoolException e) { 868 return report(e); 869 } 870 } 871 872 String getFieldName(Field f) { 873 try { 874 return f.getName(constant_pool); 875 } catch (ConstantPoolException e) { 876 return report(e); 877 } 878 } 879 880 String getName(Method m) { 881 try { 882 return m.getName(constant_pool); 883 } catch (ConstantPoolException e) { 884 return report(e); 885 } 886 } 887 888 static String getJavaName(String name) { 889 return name.replace('/', '.'); 890 } 891 892 String getSourceFile(SourceFile_attribute attr) { 893 try { 894 return attr.getSourceFile(constant_pool); 895 } catch (ConstantPoolException e) { 896 return report(e); 897 } 898 } 899 900 /** 901 * Get the value of an entry in the constant pool as a Java constant. 902 * Characters and booleans are represented by CONSTANT_Intgere entries. 903 * Character and string values are processed to escape characters outside 904 * the basic printable ASCII set. 905 * @param d the descriptor, giving the expected type of the constant 906 * @param index the index of the value in the constant pool 907 * @return a printable string containing the value of the constant. 908 */ 909 String getConstantValue(Descriptor d, int index) { 910 try { 911 ConstantPool.CPInfo cpInfo = constant_pool.get(index); 912 913 switch (cpInfo.getTag()) { 914 case ConstantPool.CONSTANT_Integer: { 915 ConstantPool.CONSTANT_Integer_info info = 916 (ConstantPool.CONSTANT_Integer_info) cpInfo; 917 String t = d.getValue(constant_pool); 918 switch (t) { 919 case "C": 920 // character 921 return getConstantCharValue((char) info.value); 922 case "Z": 923 // boolean 924 return String.valueOf(info.value == 1); 925 default: 926 // other: assume integer 927 return String.valueOf(info.value); 928 } 929 } 930 931 case ConstantPool.CONSTANT_String: { 932 ConstantPool.CONSTANT_String_info info = 933 (ConstantPool.CONSTANT_String_info) cpInfo; 934 return getConstantStringValue(info.getString()); 935 } 936 937 default: 938 return constantWriter.stringValue(cpInfo); 939 } 940 } catch (ConstantPoolException e) { 941 return "#" + index; 942 } 943 } 944 945 private String getConstantCharValue(char c) { 946 StringBuilder sb = new StringBuilder(); 947 sb.append('\''); 948 sb.append(esc(c, '\'')); 949 sb.append('\''); 950 return sb.toString(); 951 } 952 953 private String getConstantStringValue(String s) { 954 StringBuilder sb = new StringBuilder(); 955 sb.append("\""); 956 for (int i = 0; i < s.length(); i++) { 957 sb.append(esc(s.charAt(i), '"')); 958 } 959 sb.append("\""); 960 return sb.toString(); 961 } 962 963 private String esc(char c, char quote) { 964 if (32 <= c && c <= 126 && c != quote && c != '\\') 965 return String.valueOf(c); 966 else switch (c) { 967 case '\b': return "\\b"; 968 case '\n': return "\\n"; 969 case '\t': return "\\t"; 970 case '\f': return "\\f"; 971 case '\r': return "\\r"; 972 case '\\': return "\\\\"; 973 case '\'': return "\\'"; 974 case '\"': return "\\\""; 975 default: return String.format("\\u%04x", (int) c); 976 } 977 } 978 979 private final Options options; 980 private final AttributeWriter attrWriter; 981 private final CodeWriter codeWriter; 982 private final ConstantWriter constantWriter; 983 private ClassFile classFile; 984 private URI uri; 985 private long lastModified; 986 private String digestName; 987 private byte[] digest; 988 private int size; 989 private ConstantPool constant_pool; 990 private Method method; 991 }