Thu, 30 Jul 2009 09:18:55 -0700
4880672: javap does not output inner interfaces of an interface
Reviewed-by: mcimadamore
1 /*
2 * Copyright 2007-2008 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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;
52 import static com.sun.tools.classfile.AccessFlags.*;
54 /*
55 * The main javap class to write the contents of a class file as text.
56 *
57 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
58 * you write code that depends on this, you do so at your own risk.
59 * This code and its internal interfaces are subject to change or
60 * deletion without notice.</b>
61 */
62 public class ClassWriter extends BasicWriter {
63 static ClassWriter instance(Context context) {
64 ClassWriter instance = context.get(ClassWriter.class);
65 if (instance == null)
66 instance = new ClassWriter(context);
67 return instance;
68 }
70 protected ClassWriter(Context context) {
71 super(context);
72 context.put(ClassWriter.class, this);
73 options = Options.instance(context);
74 attrWriter = AttributeWriter.instance(context);
75 codeWriter = CodeWriter.instance(context);
76 constantWriter = ConstantWriter.instance(context);
77 }
79 void setDigest(String name, byte[] digest) {
80 this.digestName = name;
81 this.digest = digest;
82 }
84 void setFile(URI uri) {
85 this.uri = uri;
86 }
88 void setFileSize(int size) {
89 this.size = size;
90 }
92 void setLastModified(long lastModified) {
93 this.lastModified = lastModified;
94 }
96 protected ClassFile getClassFile() {
97 return classFile;
98 }
100 protected void setClassFile(ClassFile cf) {
101 classFile = cf;
102 constant_pool = classFile.constant_pool;
103 }
105 protected Method getMethod() {
106 return method;
107 }
109 protected void setMethod(Method m) {
110 method = m;
111 }
113 public void write(ClassFile cf) {
114 setClassFile(cf);
116 if ((options.sysInfo || options.verbose) && !options.compat) {
117 if (uri != null) {
118 if (uri.getScheme().equals("file"))
119 println("Classfile " + uri.getPath());
120 else
121 println("Classfile " + uri);
122 }
123 if (lastModified != -1) {
124 Date lm = new Date(lastModified);
125 DateFormat df = DateFormat.getDateInstance();
126 if (size > 0) {
127 println("Last modified " + df.format(lm) + "; size " + size + " bytes");
128 } else {
129 println("Last modified " + df.format(lm));
130 }
131 } else if (size > 0) {
132 println("Size " + size + " bytes");
133 }
134 if (digestName != null && digest != null) {
135 StringBuilder sb = new StringBuilder();
136 for (byte b: digest)
137 sb.append(String.format("%02x", b));
138 println(digestName + " checksum " + sb);
139 }
140 }
142 Attribute sfa = cf.getAttribute(Attribute.SourceFile);
143 if (sfa instanceof SourceFile_attribute) {
144 println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\"");
145 }
147 String name = getJavaName(classFile);
148 AccessFlags flags = cf.access_flags;
150 writeModifiers(flags.getClassModifiers());
152 if (classFile.isClass())
153 print("class ");
154 else if (classFile.isInterface())
155 print("interface ");
157 print(name);
159 Signature_attribute sigAttr = getSignature(cf.attributes);
160 if (sigAttr == null) {
161 // use info from class file header
162 if (classFile.isClass() && classFile.super_class != 0 ) {
163 String sn = getJavaSuperclassName(cf);
164 print(" extends ");
165 print(sn);
166 }
167 for (int i = 0; i < classFile.interfaces.length; i++) {
168 print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ",");
169 print(getJavaInterfaceName(classFile, i));
170 }
171 } else {
172 try {
173 Type t = sigAttr.getParsedSignature().getType(constant_pool);
174 // The signature parser cannot disambiguate between a
175 // FieldType and a ClassSignatureType that only contains a superclass type.
176 if (t instanceof Type.ClassSigType)
177 print(t);
178 else {
179 print(" extends ");
180 print(t);
181 }
182 } catch (ConstantPoolException e) {
183 print(report(e));
184 }
185 }
187 if (options.verbose) {
188 println();
189 attrWriter.write(cf, cf.attributes, constant_pool);
190 println(" minor version: " + cf.minor_version);
191 println(" major version: " + cf.major_version);
192 if (!options.compat)
193 writeList(" flags: ", flags.getClassFlags(), NEWLINE);
194 constantWriter.writeConstantPool();
195 println();
196 } else {
197 if (!options.compat)
198 print(" ");
199 }
201 println("{");
202 writeFields();
203 writeMethods();
204 println("}");
205 println();
206 }
208 protected void writeFields() {
209 for (Field f: classFile.fields) {
210 writeField(f);
211 }
212 }
214 protected void writeField(Field f) {
215 if (!options.checkAccess(f.access_flags))
216 return;
218 if (!(options.showLineAndLocalVariableTables
219 || options.showDisassembled
220 || options.verbose
221 || options.showInternalSignatures
222 || options.showAllAttrs)) {
223 print(" ");
224 }
226 AccessFlags flags = f.access_flags;
227 writeModifiers(flags.getFieldModifiers());
228 Signature_attribute sigAttr = getSignature(f.attributes);
229 if (sigAttr == null)
230 print(getFieldType(f.descriptor));
231 else {
232 try {
233 Type t = sigAttr.getParsedSignature().getType(constant_pool);
234 print(t);
235 } catch (ConstantPoolException e) {
236 // report error?
237 // fall back on non-generic descriptor
238 print(getFieldType(f.descriptor));
239 }
240 }
241 print(" ");
242 print(getFieldName(f));
243 if (options.showConstants && !options.compat) { // BUG 4111861 print static final field contents
244 Attribute a = f.attributes.get(Attribute.ConstantValue);
245 if (a instanceof ConstantValue_attribute) {
246 print(" = ");
247 ConstantValue_attribute cv = (ConstantValue_attribute) a;
248 print(getConstantValue(f.descriptor, cv.constantvalue_index));
249 }
250 }
251 print(";");
252 println();
254 if (options.showInternalSignatures)
255 println(" Signature: " + getValue(f.descriptor));
257 if (options.verbose && !options.compat)
258 writeList(" flags: ", flags.getFieldFlags(), NEWLINE);
260 if (options.showAllAttrs) {
261 for (Attribute attr: f.attributes)
262 attrWriter.write(f, attr, constant_pool);
263 println();
264 }
266 if (options.showDisassembled || options.showLineAndLocalVariableTables)
267 println();
268 }
270 protected void writeMethods() {
271 for (Method m: classFile.methods)
272 writeMethod(m);
273 }
275 protected void writeMethod(Method m) {
276 if (!options.checkAccess(m.access_flags))
277 return;
279 method = m;
281 if (!(options.showLineAndLocalVariableTables
282 || options.showDisassembled
283 || options.verbose
284 || options.showInternalSignatures
285 || options.showAllAttrs)) {
286 print(" ");
287 }
289 AccessFlags flags = m.access_flags;
291 Descriptor d;
292 Type.MethodType methodType;
293 List<? extends Type> methodExceptions;
295 Signature_attribute sigAttr = getSignature(m.attributes);
296 if (sigAttr == null) {
297 d = m.descriptor;
298 methodType = null;
299 methodExceptions = null;
300 } else {
301 Signature methodSig = sigAttr.getParsedSignature();
302 d = methodSig;
303 try {
304 methodType = (Type.MethodType) methodSig.getType(constant_pool);
305 methodExceptions = methodType.throwsTypes;
306 if (methodExceptions != null && methodExceptions.size() == 0)
307 methodExceptions = null;
308 } catch (ConstantPoolException e) {
309 // report error?
310 // fall back on standard descriptor
311 methodType = null;
312 methodExceptions = null;
313 }
314 }
316 writeModifiers(flags.getMethodModifiers());
317 if (methodType != null) {
318 writeListIfNotEmpty("<", methodType.typeArgTypes, "> ");
319 }
320 if (getName(m).equals("<init>")) {
321 print(getJavaName(classFile));
322 print(getParameterTypes(d, flags));
323 } else if (getName(m).equals("<clinit>")) {
324 print("{}");
325 } else {
326 print(getReturnType(d));
327 print(" ");
328 print(getName(m));
329 print(getParameterTypes(d, flags));
330 }
332 Attribute e_attr = m.attributes.get(Attribute.Exceptions);
333 if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
334 if (e_attr instanceof Exceptions_attribute) {
335 Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
336 if (options.compat) { // Bug XXXXXXX whitespace
337 if (!(options.showLineAndLocalVariableTables
338 || options.showDisassembled
339 || options.verbose
340 || options.showInternalSignatures
341 || options.showAllAttrs)) {
342 print(" ");
343 }
344 print(" ");
345 }
346 print(" throws ");
347 if (methodExceptions != null) { // use generic list if available
348 writeList("", methodExceptions, "");
349 } else {
350 for (int i = 0; i < exceptions.number_of_exceptions; i++) {
351 if (i > 0)
352 print(", ");
353 print(getJavaException(exceptions, i));
354 }
355 }
356 } else {
357 report("Unexpected or invalid value for Exceptions attribute");
358 }
359 }
361 print(";");
362 println();
364 if (options.showInternalSignatures)
365 println(" Signature: " + getValue(m.descriptor));
367 if (options.verbose && !options.compat)
368 writeList(" flags: ", flags.getMethodFlags(), NEWLINE);
370 Code_attribute code = null;
371 Attribute c_attr = m.attributes.get(Attribute.Code);
372 if (c_attr != null) {
373 if (c_attr instanceof Code_attribute)
374 code = (Code_attribute) c_attr;
375 else
376 report("Unexpected or invalid value for Code attribute");
377 }
379 if (options.showDisassembled && !options.showAllAttrs) {
380 if (code != null) {
381 println(" Code:");
382 codeWriter.writeInstrs(code);
383 codeWriter.writeExceptionTable(code);
384 }
385 println();
386 }
388 if (options.showLineAndLocalVariableTables) {
389 if (code != null)
390 attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
391 println();
392 if (code != null)
393 attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
394 println();
395 println();
396 }
398 if (options.showAllAttrs) {
399 Attribute[] attrs = m.attributes.attrs;
400 for (Attribute attr: attrs)
401 attrWriter.write(m, attr, constant_pool);
403 // // the following condition is to mimic old javap
404 // if (!(attrs.length > 0 &&
405 // attrs[attrs.length - 1] instanceof Exceptions_attribute))
406 println();
407 }
408 }
410 void writeModifiers(Collection<String> items) {
411 for (Object item: items) {
412 print(item);
413 print(" ");
414 }
415 }
417 void writeList(String prefix, Collection<?> items, String suffix) {
418 print(prefix);
419 String sep = "";
420 for (Object item: items) {
421 print(sep);
422 print(item);
423 sep = ", ";
424 }
425 print(suffix);
426 }
428 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
429 if (items != null && items.size() > 0)
430 writeList(prefix, items, suffix);
431 }
433 Signature_attribute getSignature(Attributes attributes) {
434 if (options.compat) // javap does not recognize recent attributes
435 return null;
436 return (Signature_attribute) attributes.get(Attribute.Signature);
437 }
439 String adjustVarargs(AccessFlags flags, String params) {
440 if (flags.is(ACC_VARARGS) && !options.compat) {
441 int i = params.lastIndexOf("[]");
442 if (i > 0)
443 return params.substring(0, i) + "..." + params.substring(i+2);
444 }
446 return params;
447 }
449 String getJavaName(ClassFile cf) {
450 try {
451 return getJavaName(cf.getName());
452 } catch (ConstantPoolException e) {
453 return report(e);
454 }
455 }
457 String getJavaSuperclassName(ClassFile cf) {
458 try {
459 return getJavaName(cf.getSuperclassName());
460 } catch (ConstantPoolException e) {
461 return report(e);
462 }
463 }
465 String getJavaInterfaceName(ClassFile cf, int index) {
466 try {
467 return getJavaName(cf.getInterfaceName(index));
468 } catch (ConstantPoolException e) {
469 return report(e);
470 }
471 }
473 String getFieldType(Descriptor d) {
474 try {
475 return d.getFieldType(constant_pool);
476 } catch (ConstantPoolException e) {
477 return report(e);
478 } catch (DescriptorException e) {
479 return report(e);
480 }
481 }
483 String getReturnType(Descriptor d) {
484 try {
485 return d.getReturnType(constant_pool);
486 } catch (ConstantPoolException e) {
487 return report(e);
488 } catch (DescriptorException e) {
489 return report(e);
490 }
491 }
493 String getParameterTypes(Descriptor d, AccessFlags flags) {
494 try {
495 return adjustVarargs(flags, d.getParameterTypes(constant_pool));
496 } catch (ConstantPoolException e) {
497 return report(e);
498 } catch (DescriptorException e) {
499 return report(e);
500 }
501 }
503 String getJavaException(Exceptions_attribute attr, int index) {
504 try {
505 return getJavaName(attr.getException(index, constant_pool));
506 } catch (ConstantPoolException e) {
507 return report(e);
508 }
509 }
511 String getValue(Descriptor d) {
512 try {
513 return d.getValue(constant_pool);
514 } catch (ConstantPoolException e) {
515 return report(e);
516 }
517 }
519 String getFieldName(Field f) {
520 try {
521 return f.getName(constant_pool);
522 } catch (ConstantPoolException e) {
523 return report(e);
524 }
525 }
527 String getName(Method m) {
528 try {
529 return m.getName(constant_pool);
530 } catch (ConstantPoolException e) {
531 return report(e);
532 }
533 }
535 static String getJavaName(String name) {
536 return name.replace('/', '.');
537 }
539 String getSourceFile(SourceFile_attribute attr) {
540 try {
541 return attr.getSourceFile(constant_pool);
542 } catch (ConstantPoolException e) {
543 return report(e);
544 }
545 }
547 /**
548 * Get the value of an entry in the constant pool as a Java constant.
549 * Characters and booleans are represented by CONSTANT_Intgere entries.
550 * Character and string values are processed to escape characters outside
551 * the basic printable ASCII set.
552 * @param d the descriptor, giving the expected type of the constant
553 * @param index the index of the value in the constant pool
554 * @return a printable string containing the value of the constant.
555 */
556 String getConstantValue(Descriptor d, int index) {
557 try {
558 ConstantPool.CPInfo cpInfo = constant_pool.get(index);
560 switch (cpInfo.getTag()) {
561 case ConstantPool.CONSTANT_Integer: {
562 ConstantPool.CONSTANT_Integer_info info =
563 (ConstantPool.CONSTANT_Integer_info) cpInfo;
564 String t = d.getValue(constant_pool);
565 if (t.equals("C")) { // character
566 return getConstantCharValue((char) info.value);
567 } else if (t.equals("Z")) { // boolean
568 return String.valueOf(info.value == 1);
569 } else { // other: assume integer
570 return String.valueOf(info.value);
571 }
572 }
574 case ConstantPool.CONSTANT_String: {
575 ConstantPool.CONSTANT_String_info info =
576 (ConstantPool.CONSTANT_String_info) cpInfo;
577 return getConstantStringValue(info.getString());
578 }
580 default:
581 return constantWriter.stringValue(cpInfo);
582 }
583 } catch (ConstantPoolException e) {
584 return "#" + index;
585 }
586 }
588 private String getConstantCharValue(char c) {
589 StringBuilder sb = new StringBuilder();
590 sb.append('\'');
591 sb.append(esc(c, '\''));
592 sb.append('\'');
593 return sb.toString();
594 }
596 private String getConstantStringValue(String s) {
597 StringBuilder sb = new StringBuilder();
598 sb.append("\"");
599 for (int i = 0; i < s.length(); i++) {
600 sb.append(esc(s.charAt(i), '"'));
601 }
602 sb.append("\"");
603 return sb.toString();
604 }
606 private String esc(char c, char quote) {
607 if (32 <= c && c <= 126 && c != quote)
608 return String.valueOf(c);
609 else switch (c) {
610 case '\b': return "\\b";
611 case '\n': return "\\n";
612 case '\t': return "\\t";
613 case '\f': return "\\f";
614 case '\r': return "\\r";
615 case '\\': return "\\\\";
616 case '\'': return "\\'";
617 case '\"': return "\\\"";
618 default: return String.format("\\u%04x", (int) c);
619 }
620 }
622 private Options options;
623 private AttributeWriter attrWriter;
624 private CodeWriter codeWriter;
625 private ConstantWriter constantWriter;
626 private ClassFile classFile;
627 private URI uri;
628 private long lastModified;
629 private String digestName;
630 private byte[] digest;
631 private int size;
632 private ConstantPool constant_pool;
633 private Method method;
634 private static final String NEWLINE = System.getProperty("line.separator", "\n");
635 }