1 /* 2 * Copyright (c) 2007, 2014, 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 34 import com.sun.tools.classfile.AccessFlags; 35 import com.sun.tools.classfile.Attribute; 36 import com.sun.tools.classfile.Attributes; 37 import com.sun.tools.classfile.ClassFile; 38 import com.sun.tools.classfile.Code_attribute; 39 import com.sun.tools.classfile.ConstantPool; 40 import com.sun.tools.classfile.ConstantPoolException; 41 import com.sun.tools.classfile.ConstantValue_attribute; 42 import com.sun.tools.classfile.Descriptor; 43 import com.sun.tools.classfile.DescriptorException; 44 import com.sun.tools.classfile.Exceptions_attribute; 45 import com.sun.tools.classfile.Field; 46 import com.sun.tools.classfile.Method; 47 import com.sun.tools.classfile.Signature; 48 import com.sun.tools.classfile.Signature_attribute; 49 import com.sun.tools.classfile.SourceFile_attribute; 50 import com.sun.tools.classfile.Type; 51 import com.sun.tools.classfile.Type.ArrayType; 52 import com.sun.tools.classfile.Type.ClassSigType; 53 import com.sun.tools.classfile.Type.ClassType; 54 import com.sun.tools.classfile.Type.MethodType; 55 import com.sun.tools.classfile.Type.SimpleType; 56 import com.sun.tools.classfile.Type.TypeParamType; 57 import com.sun.tools.classfile.Type.WildcardType; 58 59 import static com.sun.tools.classfile.AccessFlags.*; 60 61 /* 62 * The main javap class to write the contents of a class file as text. 63 * 64 * <p><b>This is NOT part of any supported API. 65 * If you write code that depends on this, you do so at your own risk. 66 * This code and its internal interfaces are subject to change or 67 * deletion without notice.</b> 68 */ 69 public class ClassWriter extends BasicWriter { 70 static ClassWriter instance(Context context) { 71 ClassWriter instance = context.get(ClassWriter.class); 72 if (instance == null) 73 instance = new ClassWriter(context); 74 return instance; 75 } 76 77 protected ClassWriter(Context context) { 78 super(context); 79 context.put(ClassWriter.class, this); 80 options = Options.instance(context); 81 attrWriter = AttributeWriter.instance(context); 82 codeWriter = CodeWriter.instance(context); 83 constantWriter = ConstantWriter.instance(context); 84 } 85 86 void setDigest(String name, byte[] digest) { 87 this.digestName = name; 88 this.digest = digest; 89 } 90 91 void setFile(URI uri) { 92 this.uri = uri; 93 } 94 95 void setFileSize(int size) { 96 this.size = size; 97 } 98 99 void setLastModified(long lastModified) { 100 this.lastModified = lastModified; 101 } 102 103 protected ClassFile getClassFile() { 104 return classFile; 105 } 106 107 protected void setClassFile(ClassFile cf) { 108 classFile = cf; 109 constant_pool = classFile.constant_pool; 110 } 111 112 protected Method getMethod() { 113 return method; 114 } 115 116 protected void setMethod(Method m) { 117 method = m; 118 } 119 120 public void write(ClassFile cf) { 121 setClassFile(cf); 122 123 if (options.sysInfo || options.verbose) { 124 if (uri != null) { 125 if (uri.getScheme().equals("file")) 126 println("Classfile " + uri.getPath()); 127 else 128 println("Classfile " + uri); 129 } 130 indent(+1); 131 if (lastModified != -1) { 132 Date lm = new Date(lastModified); 133 DateFormat df = DateFormat.getDateInstance(); 134 if (size > 0) { 135 println("Last modified " + df.format(lm) + "; size " + size + " bytes"); 136 } else { 137 println("Last modified " + df.format(lm)); 138 } 139 } else if (size > 0) { 140 println("Size " + size + " bytes"); 141 } 142 if (digestName != null && digest != null) { 143 StringBuilder sb = new StringBuilder(); 144 for (byte b: digest) 145 sb.append(String.format("%02x", b)); 146 println(digestName + " checksum " + sb); 147 } 148 } 149 150 Attribute sfa = cf.getAttribute(Attribute.SourceFile); 151 if (sfa instanceof SourceFile_attribute) { 152 println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\""); 153 } 154 155 if (options.sysInfo || options.verbose) { 156 indent(-1); 157 } 158 159 String name = getJavaName(classFile); 160 AccessFlags flags = cf.access_flags; 161 162 writeModifiers(flags.getClassModifiers()); 163 164 if (classFile.isClass()) 165 print("class "); 166 else if (classFile.isInterface()) 167 print("interface "); 168 169 print(name); 170 171 Signature_attribute sigAttr = getSignature(cf.attributes); 172 if (sigAttr == null) { 173 // use info from class file header 174 if (classFile.isClass() && classFile.super_class != 0 ) { 175 String sn = getJavaSuperclassName(cf); 176 if (!sn.equals("java.lang.Object")) { 177 print(" extends "); 178 print(sn); 179 } 180 } 181 for (int i = 0; i < classFile.interfaces.length; i++) { 182 print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ","); 183 print(getJavaInterfaceName(classFile, i)); 184 } 185 } else { 186 try { 187 Type t = sigAttr.getParsedSignature().getType(constant_pool); 188 JavaTypePrinter p = new JavaTypePrinter(classFile.isInterface()); 189 // The signature parser cannot disambiguate between a 190 // FieldType and a ClassSignatureType that only contains a superclass type. 191 if (t instanceof Type.ClassSigType) { 192 print(p.print(t)); 193 } else if (options.verbose || !t.isObject()) { 194 print(" extends "); 195 print(p.print(t)); 196 } 197 } catch (ConstantPoolException e) { 198 print(report(e)); 199 } 200 } 201 202 if (options.verbose) { 203 println(); 204 indent(+1); 205 println("minor version: " + cf.minor_version); 206 println("major version: " + cf.major_version); 207 writeList("flags: ", flags.getClassFlags(), "\n"); 208 indent(-1); 209 constantWriter.writeConstantPool(); 210 } else { 211 print(" "); 212 } 213 214 println("{"); 215 indent(+1); 216 writeFields(); 217 writeMethods(); 218 indent(-1); 219 println("}"); 220 221 if (options.verbose) { 222 attrWriter.write(cf, cf.attributes, constant_pool); 223 } 224 } 225 // where 226 class JavaTypePrinter implements Type.Visitor<StringBuilder,StringBuilder> { 227 boolean isInterface; 228 229 JavaTypePrinter(boolean isInterface) { 230 this.isInterface = isInterface; 231 } 232 233 String print(Type t) { 234 return t.accept(this, new StringBuilder()).toString(); 235 } 236 237 String printTypeArgs(List<? extends TypeParamType> typeParamTypes) { 238 StringBuilder builder = new StringBuilder(); 239 appendIfNotEmpty(builder, "<", typeParamTypes, "> "); 240 return builder.toString(); 241 } 242 243 public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) { 244 sb.append(getJavaName(type.name)); 245 return sb; 246 } 247 248 public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) { 249 append(sb, type.elemType); 250 sb.append("[]"); 251 return sb; 252 } 253 254 public StringBuilder visitMethodType(MethodType type, StringBuilder sb) { 255 appendIfNotEmpty(sb, "<", type.typeParamTypes, "> "); 256 append(sb, type.returnType); 257 append(sb, " (", type.paramTypes, ")"); 258 appendIfNotEmpty(sb, " throws ", type.throwsTypes, ""); 259 return sb; 260 } 261 262 public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) { 263 appendIfNotEmpty(sb, "<", type.typeParamTypes, ">"); 264 if (isInterface) { 265 appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, ""); 266 } else { 267 if (type.superclassType != null 268 && (options.verbose || !type.superclassType.isObject())) { 269 sb.append(" extends "); 270 append(sb, type.superclassType); 271 } 272 appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, ""); 273 } 274 return sb; 275 } 276 277 public StringBuilder visitClassType(ClassType type, StringBuilder sb) { 278 if (type.outerType != null) { 279 append(sb, type.outerType); 280 sb.append("."); 281 } 282 sb.append(getJavaName(type.name)); 283 appendIfNotEmpty(sb, "<", type.typeArgs, ">"); 284 return sb; 285 } 286 287 public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) { 288 sb.append(type.name); 289 String sep = " extends "; 290 if (type.classBound != null 291 && (options.verbose || !type.classBound.isObject())) { 292 sb.append(sep); 293 append(sb, type.classBound); 294 sep = " & "; 295 } 296 if (type.interfaceBounds != null) { 297 for (Type bound: type.interfaceBounds) { 298 sb.append(sep); 299 append(sb, bound); 300 sep = " & "; 301 } 302 } 303 return sb; 304 } 305 306 public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) { 307 switch (type.kind) { 308 case UNBOUNDED: 309 sb.append("?"); 310 break; 311 case EXTENDS: 312 sb.append("? extends "); 313 append(sb, type.boundType); 314 break; 315 case SUPER: 316 sb.append("? super "); 317 append(sb, type.boundType); 318 break; 319 default: 320 throw new AssertionError(); 321 } 322 return sb; 323 } 324 325 private void append(StringBuilder sb, Type t) { 326 t.accept(this, sb); 327 } 328 329 private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) { 330 sb.append(prefix); 331 String sep = ""; 332 for (Type t: list) { 333 sb.append(sep); 334 append(sb, t); 335 sep = ", "; 336 } 337 sb.append(suffix); 338 } 339 340 private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) { 341 if (!isEmpty(list)) 342 append(sb, prefix, list, suffix); 343 } 344 345 private boolean isEmpty(List<? extends Type> list) { 346 return (list == null || list.isEmpty()); 347 } 348 } 349 350 protected void writeFields() { 351 for (Field f: classFile.fields) { 352 writeField(f); 353 } 354 } 355 356 protected void writeField(Field f) { 357 if (!options.checkAccess(f.access_flags)) 358 return; 359 360 AccessFlags flags = f.access_flags; 361 writeModifiers(flags.getFieldModifiers()); 362 Signature_attribute sigAttr = getSignature(f.attributes); 363 if (sigAttr == null) 364 print(getJavaFieldType(f.descriptor)); 365 else { 366 try { 367 Type t = sigAttr.getParsedSignature().getType(constant_pool); 368 print(getJavaName(t.toString())); 369 } catch (ConstantPoolException e) { 370 // report error? 371 // fall back on non-generic descriptor 372 print(getJavaFieldType(f.descriptor)); 373 } 374 } 375 print(" "); 376 print(getFieldName(f)); 377 if (options.showConstants) { 378 Attribute a = f.attributes.get(Attribute.ConstantValue); 379 if (a instanceof ConstantValue_attribute) { 380 print(" = "); 381 ConstantValue_attribute cv = (ConstantValue_attribute) a; 382 print(getConstantValue(f.descriptor, cv.constantvalue_index)); 383 } 384 } 385 print(";"); 386 println(); 387 388 indent(+1); 389 390 boolean showBlank = false; 391 392 if (options.showDescriptors) 393 println("descriptor: " + getValue(f.descriptor)); 394 395 if (options.verbose) 396 writeList("flags: ", flags.getFieldFlags(), "\n"); 397 398 if (options.showAllAttrs) { 399 for (Attribute attr: f.attributes) 400 attrWriter.write(f, attr, constant_pool); 401 showBlank = true; 402 } 403 404 indent(-1); 405 406 if (showBlank || options.showDisassembled || options.showLineAndLocalVariableTables) 407 println(); 408 } 409 410 protected void writeMethods() { 411 for (Method m: classFile.methods) 412 writeMethod(m); 413 setPendingNewline(false); 414 } 415 416 protected void writeMethod(Method m) { 417 if (!options.checkAccess(m.access_flags)) 418 return; 419 420 method = m; 421 422 AccessFlags flags = m.access_flags; 423 424 Descriptor d; 425 Type.MethodType methodType; 426 List<? extends Type> methodExceptions; 427 428 Signature_attribute sigAttr = getSignature(m.attributes); 429 if (sigAttr == null) { 430 d = m.descriptor; 431 methodType = null; 432 methodExceptions = null; 433 } else { 434 Signature methodSig = sigAttr.getParsedSignature(); 435 d = methodSig; 436 try { 437 methodType = (Type.MethodType) methodSig.getType(constant_pool); 438 methodExceptions = methodType.throwsTypes; 439 if (methodExceptions != null && methodExceptions.isEmpty()) 440 methodExceptions = null; 441 } catch (ConstantPoolException e) { 442 // report error? 443 // fall back on standard descriptor 444 methodType = null; 445 methodExceptions = null; 446 } 447 } 448 449 writeModifiers(flags.getMethodModifiers()); 450 if (methodType != null) { 451 print(new JavaTypePrinter(false).printTypeArgs(methodType.typeParamTypes)); 452 } 453 if (getName(m).equals("<init>")) { 454 print(getJavaName(classFile)); 455 print(getJavaParameterTypes(d, flags)); 456 } else if (getName(m).equals("<clinit>")) { 457 print("{}"); 458 } else { 459 print(getJavaReturnType(d)); 460 print(" "); 461 print(getName(m)); 462 print(getJavaParameterTypes(d, flags)); 463 } 464 465 Attribute e_attr = m.attributes.get(Attribute.Exceptions); 466 if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions 467 if (e_attr instanceof Exceptions_attribute) { 468 Exceptions_attribute exceptions = (Exceptions_attribute) e_attr; 469 print(" throws "); 470 if (methodExceptions != null) { // use generic list if available 471 writeList("", methodExceptions, ""); 472 } else { 473 for (int i = 0; i < exceptions.number_of_exceptions; i++) { 474 if (i > 0) 475 print(", "); 476 print(getJavaException(exceptions, i)); 477 } 478 } 479 } else { 480 report("Unexpected or invalid value for Exceptions attribute"); 481 } 482 } 483 484 println(";"); 485 486 indent(+1); 487 488 if (options.showDescriptors) { 489 println("descriptor: " + getValue(m.descriptor)); 490 } 491 492 if (options.verbose) { 493 writeList("flags: ", flags.getMethodFlags(), "\n"); 494 } 495 496 Code_attribute code = null; 497 Attribute c_attr = m.attributes.get(Attribute.Code); 498 if (c_attr != null) { 499 if (c_attr instanceof Code_attribute) 500 code = (Code_attribute) c_attr; 501 else 502 report("Unexpected or invalid value for Code attribute"); 503 } 504 505 if (options.showAllAttrs) { 506 Attribute[] attrs = m.attributes.attrs; 507 for (Attribute attr: attrs) 508 attrWriter.write(m, attr, constant_pool); 509 } else if (code != null) { 510 if (options.showDisassembled) { 511 println("Code:"); 512 codeWriter.writeInstrs(code); 513 codeWriter.writeExceptionTable(code); 514 } 515 516 if (options.showLineAndLocalVariableTables) { 517 attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool); 518 attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool); 519 } 520 } 521 522 indent(-1); 523 524 // set pendingNewline to write a newline before the next method (if any) 525 // if a separator is desired 526 setPendingNewline( 527 options.showDisassembled || 528 options.showAllAttrs || 529 options.showDescriptors || 530 options.showLineAndLocalVariableTables || 531 options.verbose); 532 } 533 534 void writeModifiers(Collection<String> items) { 535 for (Object item: items) { 536 print(item); 537 print(" "); 538 } 539 } 540 541 void writeList(String prefix, Collection<?> items, String suffix) { 542 print(prefix); 543 String sep = ""; 544 for (Object item: items) { 545 print(sep); 546 print(item); 547 sep = ", "; 548 } 549 print(suffix); 550 } 551 552 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) { 553 if (items != null && items.size() > 0) 554 writeList(prefix, items, suffix); 555 } 556 557 Signature_attribute getSignature(Attributes attributes) { 558 return (Signature_attribute) attributes.get(Attribute.Signature); 559 } 560 561 String adjustVarargs(AccessFlags flags, String params) { 562 if (flags.is(ACC_VARARGS)) { 563 int i = params.lastIndexOf("[]"); 564 if (i > 0) 565 return params.substring(0, i) + "..." + params.substring(i+2); 566 } 567 568 return params; 569 } 570 571 String getJavaName(ClassFile cf) { 572 try { 573 return getJavaName(cf.getName()); 574 } catch (ConstantPoolException e) { 575 return report(e); 576 } 577 } 578 579 String getJavaSuperclassName(ClassFile cf) { 580 try { 581 return getJavaName(cf.getSuperclassName()); 582 } catch (ConstantPoolException e) { 583 return report(e); 584 } 585 } 586 587 String getJavaInterfaceName(ClassFile cf, int index) { 588 try { 589 return getJavaName(cf.getInterfaceName(index)); 590 } catch (ConstantPoolException e) { 591 return report(e); 592 } 593 } 594 595 String getJavaFieldType(Descriptor d) { 596 try { 597 return getJavaName(d.getFieldType(constant_pool)); 598 } catch (ConstantPoolException e) { 599 return report(e); 600 } catch (DescriptorException e) { 601 return report(e); 602 } 603 } 604 605 String getJavaReturnType(Descriptor d) { 606 try { 607 return getJavaName(d.getReturnType(constant_pool)); 608 } catch (ConstantPoolException e) { 609 return report(e); 610 } catch (DescriptorException e) { 611 return report(e); 612 } 613 } 614 615 String getJavaParameterTypes(Descriptor d, AccessFlags flags) { 616 try { 617 return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool))); 618 } catch (ConstantPoolException e) { 619 return report(e); 620 } catch (DescriptorException e) { 621 return report(e); 622 } 623 } 624 625 String getJavaException(Exceptions_attribute attr, int index) { 626 try { 627 return getJavaName(attr.getException(index, constant_pool)); 628 } catch (ConstantPoolException e) { 629 return report(e); 630 } 631 } 632 633 String getValue(Descriptor d) { 634 try { 635 return d.getValue(constant_pool); 636 } catch (ConstantPoolException e) { 637 return report(e); 638 } 639 } 640 641 String getFieldName(Field f) { 642 try { 643 return f.getName(constant_pool); 644 } catch (ConstantPoolException e) { 645 return report(e); 646 } 647 } 648 649 String getName(Method m) { 650 try { 651 return m.getName(constant_pool); 652 } catch (ConstantPoolException e) { 653 return report(e); 654 } 655 } 656 657 static String getJavaName(String name) { 658 return name.replace('/', '.'); 659 } 660 661 String getSourceFile(SourceFile_attribute attr) { 662 try { 663 return attr.getSourceFile(constant_pool); 664 } catch (ConstantPoolException e) { 665 return report(e); 666 } 667 } 668 669 /** 670 * Get the value of an entry in the constant pool as a Java constant. 671 * Characters and booleans are represented by CONSTANT_Intgere entries. 672 * Character and string values are processed to escape characters outside 673 * the basic printable ASCII set. 674 * @param d the descriptor, giving the expected type of the constant 675 * @param index the index of the value in the constant pool 676 * @return a printable string containing the value of the constant. 677 */ 678 String getConstantValue(Descriptor d, int index) { 679 try { 680 ConstantPool.CPInfo cpInfo = constant_pool.get(index); 681 682 switch (cpInfo.getTag()) { 683 case ConstantPool.CONSTANT_Integer: { 684 ConstantPool.CONSTANT_Integer_info info = 685 (ConstantPool.CONSTANT_Integer_info) cpInfo; 686 String t = d.getValue(constant_pool); 687 if (t.equals("C")) { // character 688 return getConstantCharValue((char) info.value); 689 } else if (t.equals("Z")) { // boolean 690 return String.valueOf(info.value == 1); 691 } else { // other: assume integer 692 return String.valueOf(info.value); 693 } 694 } 695 696 case ConstantPool.CONSTANT_String: { 697 ConstantPool.CONSTANT_String_info info = 698 (ConstantPool.CONSTANT_String_info) cpInfo; 699 return getConstantStringValue(info.getString()); 700 } 701 702 default: 703 return constantWriter.stringValue(cpInfo); 704 } 705 } catch (ConstantPoolException e) { 706 return "#" + index; 707 } 708 } 709 710 private String getConstantCharValue(char c) { 711 StringBuilder sb = new StringBuilder(); 712 sb.append('\''); 713 sb.append(esc(c, '\'')); 714 sb.append('\''); 715 return sb.toString(); 716 } 717 718 private String getConstantStringValue(String s) { 719 StringBuilder sb = new StringBuilder(); 720 sb.append("\""); 721 for (int i = 0; i < s.length(); i++) { 722 sb.append(esc(s.charAt(i), '"')); 723 } 724 sb.append("\""); 725 return sb.toString(); 726 } 727 728 private String esc(char c, char quote) { 729 if (32 <= c && c <= 126 && c != quote) 730 return String.valueOf(c); 731 else switch (c) { 732 case '\b': return "\\b"; 733 case '\n': return "\\n"; 734 case '\t': return "\\t"; 735 case '\f': return "\\f"; 736 case '\r': return "\\r"; 737 case '\\': return "\\\\"; 738 case '\'': return "\\'"; 739 case '\"': return "\\\""; 740 default: return String.format("\\u%04x", (int) c); 741 } 742 } 743 744 private Options options; 745 private AttributeWriter attrWriter; 746 private CodeWriter codeWriter; 747 private ConstantWriter constantWriter; 748 private ClassFile classFile; 749 private URI uri; 750 private long lastModified; 751 private String digestName; 752 private byte[] digest; 753 private int size; 754 private ConstantPool constant_pool; 755 private Method method; 756 }