Fri, 04 Oct 2013 16:08:18 -0700
8003537: javap use internal class name when printing bound of type variable
Reviewed-by: jjg
1 /*
2 * Copyright (c) 2007, 2013, 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 */
26 package com.sun.tools.javap;
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;
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;
59 import static com.sun.tools.classfile.AccessFlags.*;
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 }
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 }
86 void setDigest(String name, byte[] digest) {
87 this.digestName = name;
88 this.digest = digest;
89 }
91 void setFile(URI uri) {
92 this.uri = uri;
93 }
95 void setFileSize(int size) {
96 this.size = size;
97 }
99 void setLastModified(long lastModified) {
100 this.lastModified = lastModified;
101 }
103 protected ClassFile getClassFile() {
104 return classFile;
105 }
107 protected void setClassFile(ClassFile cf) {
108 classFile = cf;
109 constant_pool = classFile.constant_pool;
110 }
112 protected Method getMethod() {
113 return method;
114 }
116 protected void setMethod(Method m) {
117 method = m;
118 }
120 public void write(ClassFile cf) {
121 setClassFile(cf);
123 if ((options.sysInfo || options.verbose) && !options.compat) {
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 }
150 Attribute sfa = cf.getAttribute(Attribute.SourceFile);
151 if (sfa instanceof SourceFile_attribute) {
152 println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\"");
153 }
155 if ((options.sysInfo || options.verbose) && !options.compat) {
156 indent(-1);
157 }
159 String name = getJavaName(classFile);
160 AccessFlags flags = cf.access_flags;
162 writeModifiers(flags.getClassModifiers());
164 if (classFile.isClass())
165 print("class ");
166 else if (classFile.isInterface())
167 print("interface ");
169 print(name);
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 }
202 if (options.verbose) {
203 println();
204 indent(+1);
205 attrWriter.write(cf, cf.attributes, constant_pool);
206 println("minor version: " + cf.minor_version);
207 println("major version: " + cf.major_version);
208 if (!options.compat)
209 writeList("flags: ", flags.getClassFlags(), "\n");
210 indent(-1);
211 constantWriter.writeConstantPool();
212 } else {
213 print(" ");
214 }
216 println("{");
217 indent(+1);
218 writeFields();
219 writeMethods();
220 indent(-1);
221 println("}");
222 }
223 // where
224 class JavaTypePrinter implements Type.Visitor<StringBuilder,StringBuilder> {
225 boolean isInterface;
227 JavaTypePrinter(boolean isInterface) {
228 this.isInterface = isInterface;
229 }
231 String print(Type t) {
232 return t.accept(this, new StringBuilder()).toString();
233 }
235 String printTypeArgs(List<? extends TypeParamType> typeParamTypes) {
236 StringBuilder builder = new StringBuilder();
237 appendIfNotEmpty(builder, "<", typeParamTypes, "> ");
238 return builder.toString();
239 }
241 public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) {
242 sb.append(getJavaName(type.name));
243 return sb;
244 }
246 public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) {
247 append(sb, type.elemType);
248 sb.append("[]");
249 return sb;
250 }
252 public StringBuilder visitMethodType(MethodType type, StringBuilder sb) {
253 appendIfNotEmpty(sb, "<", type.typeParamTypes, "> ");
254 append(sb, type.returnType);
255 append(sb, " (", type.paramTypes, ")");
256 appendIfNotEmpty(sb, " throws ", type.throwsTypes, "");
257 return sb;
258 }
260 public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) {
261 appendIfNotEmpty(sb, "<", type.typeParamTypes, ">");
262 if (isInterface) {
263 appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, "");
264 } else {
265 if (type.superclassType != null
266 && (options.verbose || !type.superclassType.isObject())) {
267 sb.append(" extends ");
268 append(sb, type.superclassType);
269 }
270 appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, "");
271 }
272 return sb;
273 }
275 public StringBuilder visitClassType(ClassType type, StringBuilder sb) {
276 if (type.outerType != null) {
277 append(sb, type.outerType);
278 sb.append(".");
279 }
280 sb.append(getJavaName(type.name));
281 appendIfNotEmpty(sb, "<", type.typeArgs, ">");
282 return sb;
283 }
285 public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) {
286 sb.append(type.name);
287 String sep = " extends ";
288 if (type.classBound != null
289 && (options.verbose || !type.classBound.isObject())) {
290 sb.append(sep);
291 append(sb, type.classBound);
292 sep = " & ";
293 }
294 if (type.interfaceBounds != null) {
295 for (Type bound: type.interfaceBounds) {
296 sb.append(sep);
297 append(sb, bound);
298 sep = " & ";
299 }
300 }
301 return sb;
302 }
304 public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) {
305 switch (type.kind) {
306 case UNBOUNDED:
307 sb.append("?");
308 break;
309 case EXTENDS:
310 sb.append("? extends ");
311 append(sb, type.boundType);
312 break;
313 case SUPER:
314 sb.append("? super ");
315 append(sb, type.boundType);
316 break;
317 default:
318 throw new AssertionError();
319 }
320 return sb;
321 }
323 private void append(StringBuilder sb, Type t) {
324 t.accept(this, sb);
325 }
327 private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
328 sb.append(prefix);
329 String sep = "";
330 for (Type t: list) {
331 sb.append(sep);
332 append(sb, t);
333 sep = ", ";
334 }
335 sb.append(suffix);
336 }
338 private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
339 if (!isEmpty(list))
340 append(sb, prefix, list, suffix);
341 }
343 private boolean isEmpty(List<? extends Type> list) {
344 return (list == null || list.isEmpty());
345 }
346 }
348 protected void writeFields() {
349 for (Field f: classFile.fields) {
350 writeField(f);
351 }
352 }
354 protected void writeField(Field f) {
355 if (!options.checkAccess(f.access_flags))
356 return;
358 AccessFlags flags = f.access_flags;
359 writeModifiers(flags.getFieldModifiers());
360 Signature_attribute sigAttr = getSignature(f.attributes);
361 if (sigAttr == null)
362 print(getJavaFieldType(f.descriptor));
363 else {
364 try {
365 Type t = sigAttr.getParsedSignature().getType(constant_pool);
366 print(getJavaName(t.toString()));
367 } catch (ConstantPoolException e) {
368 // report error?
369 // fall back on non-generic descriptor
370 print(getJavaFieldType(f.descriptor));
371 }
372 }
373 print(" ");
374 print(getFieldName(f));
375 if (options.showConstants && !options.compat) { // BUG 4111861 print static final field contents
376 Attribute a = f.attributes.get(Attribute.ConstantValue);
377 if (a instanceof ConstantValue_attribute) {
378 print(" = ");
379 ConstantValue_attribute cv = (ConstantValue_attribute) a;
380 print(getConstantValue(f.descriptor, cv.constantvalue_index));
381 }
382 }
383 print(";");
384 println();
386 indent(+1);
388 if (options.showDescriptors)
389 println("descriptor: " + getValue(f.descriptor));
391 if (options.verbose && !options.compat)
392 writeList("flags: ", flags.getFieldFlags(), "\n");
394 if (options.showAllAttrs) {
395 for (Attribute attr: f.attributes)
396 attrWriter.write(f, attr, constant_pool);
397 println();
398 }
400 indent(-1);
402 if (options.showDisassembled || options.showLineAndLocalVariableTables)
403 println();
404 }
406 protected void writeMethods() {
407 for (Method m: classFile.methods)
408 writeMethod(m);
409 setPendingNewline(false);
410 }
412 protected void writeMethod(Method m) {
413 if (!options.checkAccess(m.access_flags))
414 return;
416 method = m;
418 AccessFlags flags = m.access_flags;
420 Descriptor d;
421 Type.MethodType methodType;
422 List<? extends Type> methodExceptions;
424 Signature_attribute sigAttr = getSignature(m.attributes);
425 if (sigAttr == null) {
426 d = m.descriptor;
427 methodType = null;
428 methodExceptions = null;
429 } else {
430 Signature methodSig = sigAttr.getParsedSignature();
431 d = methodSig;
432 try {
433 methodType = (Type.MethodType) methodSig.getType(constant_pool);
434 methodExceptions = methodType.throwsTypes;
435 if (methodExceptions != null && methodExceptions.isEmpty())
436 methodExceptions = null;
437 } catch (ConstantPoolException e) {
438 // report error?
439 // fall back on standard descriptor
440 methodType = null;
441 methodExceptions = null;
442 }
443 }
445 writeModifiers(flags.getMethodModifiers());
446 if (methodType != null) {
447 print(new JavaTypePrinter(false).printTypeArgs(methodType.typeParamTypes));
448 }
449 if (getName(m).equals("<init>")) {
450 print(getJavaName(classFile));
451 print(getJavaParameterTypes(d, flags));
452 } else if (getName(m).equals("<clinit>")) {
453 print("{}");
454 } else {
455 print(getJavaReturnType(d));
456 print(" ");
457 print(getName(m));
458 print(getJavaParameterTypes(d, flags));
459 }
461 Attribute e_attr = m.attributes.get(Attribute.Exceptions);
462 if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
463 if (e_attr instanceof Exceptions_attribute) {
464 Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
465 print(" throws ");
466 if (methodExceptions != null) { // use generic list if available
467 writeList("", methodExceptions, "");
468 } else {
469 for (int i = 0; i < exceptions.number_of_exceptions; i++) {
470 if (i > 0)
471 print(", ");
472 print(getJavaException(exceptions, i));
473 }
474 }
475 } else {
476 report("Unexpected or invalid value for Exceptions attribute");
477 }
478 }
480 println(";");
482 indent(+1);
484 if (options.showDescriptors) {
485 println("descriptor: " + getValue(m.descriptor));
486 }
488 if (options.verbose && !options.compat) {
489 writeList("flags: ", flags.getMethodFlags(), "\n");
490 }
492 Code_attribute code = null;
493 Attribute c_attr = m.attributes.get(Attribute.Code);
494 if (c_attr != null) {
495 if (c_attr instanceof Code_attribute)
496 code = (Code_attribute) c_attr;
497 else
498 report("Unexpected or invalid value for Code attribute");
499 }
501 if (options.showDisassembled && !options.showAllAttrs) {
502 if (code != null) {
503 println("Code:");
504 codeWriter.writeInstrs(code);
505 codeWriter.writeExceptionTable(code);
506 }
507 }
509 if (options.showLineAndLocalVariableTables) {
510 if (code != null) {
511 attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
512 attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
513 }
514 }
516 if (options.showAllAttrs) {
517 Attribute[] attrs = m.attributes.attrs;
518 for (Attribute attr: attrs)
519 attrWriter.write(m, attr, constant_pool);
520 }
522 indent(-1);
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 }
534 void writeModifiers(Collection<String> items) {
535 for (Object item: items) {
536 print(item);
537 print(" ");
538 }
539 }
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 }
552 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
553 if (items != null && items.size() > 0)
554 writeList(prefix, items, suffix);
555 }
557 Signature_attribute getSignature(Attributes attributes) {
558 if (options.compat) // javap does not recognize recent attributes
559 return null;
560 return (Signature_attribute) attributes.get(Attribute.Signature);
561 }
563 String adjustVarargs(AccessFlags flags, String params) {
564 if (flags.is(ACC_VARARGS) && !options.compat) {
565 int i = params.lastIndexOf("[]");
566 if (i > 0)
567 return params.substring(0, i) + "..." + params.substring(i+2);
568 }
570 return params;
571 }
573 String getJavaName(ClassFile cf) {
574 try {
575 return getJavaName(cf.getName());
576 } catch (ConstantPoolException e) {
577 return report(e);
578 }
579 }
581 String getJavaSuperclassName(ClassFile cf) {
582 try {
583 return getJavaName(cf.getSuperclassName());
584 } catch (ConstantPoolException e) {
585 return report(e);
586 }
587 }
589 String getJavaInterfaceName(ClassFile cf, int index) {
590 try {
591 return getJavaName(cf.getInterfaceName(index));
592 } catch (ConstantPoolException e) {
593 return report(e);
594 }
595 }
597 String getJavaFieldType(Descriptor d) {
598 try {
599 return getJavaName(d.getFieldType(constant_pool));
600 } catch (ConstantPoolException e) {
601 return report(e);
602 } catch (DescriptorException e) {
603 return report(e);
604 }
605 }
607 String getJavaReturnType(Descriptor d) {
608 try {
609 return getJavaName(d.getReturnType(constant_pool));
610 } catch (ConstantPoolException e) {
611 return report(e);
612 } catch (DescriptorException e) {
613 return report(e);
614 }
615 }
617 String getJavaParameterTypes(Descriptor d, AccessFlags flags) {
618 try {
619 return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool)));
620 } catch (ConstantPoolException e) {
621 return report(e);
622 } catch (DescriptorException e) {
623 return report(e);
624 }
625 }
627 String getJavaException(Exceptions_attribute attr, int index) {
628 try {
629 return getJavaName(attr.getException(index, constant_pool));
630 } catch (ConstantPoolException e) {
631 return report(e);
632 }
633 }
635 String getValue(Descriptor d) {
636 try {
637 return d.getValue(constant_pool);
638 } catch (ConstantPoolException e) {
639 return report(e);
640 }
641 }
643 String getFieldName(Field f) {
644 try {
645 return f.getName(constant_pool);
646 } catch (ConstantPoolException e) {
647 return report(e);
648 }
649 }
651 String getName(Method m) {
652 try {
653 return m.getName(constant_pool);
654 } catch (ConstantPoolException e) {
655 return report(e);
656 }
657 }
659 static String getJavaName(String name) {
660 return name.replace('/', '.');
661 }
663 String getSourceFile(SourceFile_attribute attr) {
664 try {
665 return attr.getSourceFile(constant_pool);
666 } catch (ConstantPoolException e) {
667 return report(e);
668 }
669 }
671 /**
672 * Get the value of an entry in the constant pool as a Java constant.
673 * Characters and booleans are represented by CONSTANT_Intgere entries.
674 * Character and string values are processed to escape characters outside
675 * the basic printable ASCII set.
676 * @param d the descriptor, giving the expected type of the constant
677 * @param index the index of the value in the constant pool
678 * @return a printable string containing the value of the constant.
679 */
680 String getConstantValue(Descriptor d, int index) {
681 try {
682 ConstantPool.CPInfo cpInfo = constant_pool.get(index);
684 switch (cpInfo.getTag()) {
685 case ConstantPool.CONSTANT_Integer: {
686 ConstantPool.CONSTANT_Integer_info info =
687 (ConstantPool.CONSTANT_Integer_info) cpInfo;
688 String t = d.getValue(constant_pool);
689 if (t.equals("C")) { // character
690 return getConstantCharValue((char) info.value);
691 } else if (t.equals("Z")) { // boolean
692 return String.valueOf(info.value == 1);
693 } else { // other: assume integer
694 return String.valueOf(info.value);
695 }
696 }
698 case ConstantPool.CONSTANT_String: {
699 ConstantPool.CONSTANT_String_info info =
700 (ConstantPool.CONSTANT_String_info) cpInfo;
701 return getConstantStringValue(info.getString());
702 }
704 default:
705 return constantWriter.stringValue(cpInfo);
706 }
707 } catch (ConstantPoolException e) {
708 return "#" + index;
709 }
710 }
712 private String getConstantCharValue(char c) {
713 StringBuilder sb = new StringBuilder();
714 sb.append('\'');
715 sb.append(esc(c, '\''));
716 sb.append('\'');
717 return sb.toString();
718 }
720 private String getConstantStringValue(String s) {
721 StringBuilder sb = new StringBuilder();
722 sb.append("\"");
723 for (int i = 0; i < s.length(); i++) {
724 sb.append(esc(s.charAt(i), '"'));
725 }
726 sb.append("\"");
727 return sb.toString();
728 }
730 private String esc(char c, char quote) {
731 if (32 <= c && c <= 126 && c != quote)
732 return String.valueOf(c);
733 else switch (c) {
734 case '\b': return "\\b";
735 case '\n': return "\\n";
736 case '\t': return "\\t";
737 case '\f': return "\\f";
738 case '\r': return "\\r";
739 case '\\': return "\\\\";
740 case '\'': return "\\'";
741 case '\"': return "\\\"";
742 default: return String.format("\\u%04x", (int) c);
743 }
744 }
746 private Options options;
747 private AttributeWriter attrWriter;
748 private CodeWriter codeWriter;
749 private ConstantWriter constantWriter;
750 private ClassFile classFile;
751 private URI uri;
752 private long lastModified;
753 private String digestName;
754 private byte[] digest;
755 private int size;
756 private ConstantPool constant_pool;
757 private Method method;
758 }