Wed, 17 Nov 2010 15:07:43 -0800
7000973: isBogus needs to be called on the to-be-returned entry, not on the current entry
Reviewed-by: jjg
Contributed-by: jan.lahoda@oracle.com
1 /*
2 * Copyright (c) 2007, 2008, 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;
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 supported API.
58 * If 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 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 + " bytes");
129 } else {
130 println("Last modified " + df.format(lm));
131 }
132 } else if (size > 0) {
133 println("Size " + size + " bytes");
134 }
135 if (digestName != null && digest != null) {
136 StringBuilder sb = new StringBuilder();
137 for (byte b: digest)
138 sb.append(String.format("%02x", b));
139 println(digestName + " checksum " + sb);
140 }
141 }
143 Attribute sfa = cf.getAttribute(Attribute.SourceFile);
144 if (sfa instanceof SourceFile_attribute) {
145 println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\"");
146 }
148 if ((options.sysInfo || options.verbose) && !options.compat) {
149 indent(-1);
150 }
152 String name = getJavaName(classFile);
153 AccessFlags flags = cf.access_flags;
155 writeModifiers(flags.getClassModifiers());
157 if (classFile.isClass())
158 print("class ");
159 else if (classFile.isInterface())
160 print("interface ");
162 print(name);
164 Signature_attribute sigAttr = getSignature(cf.attributes);
165 if (sigAttr == null) {
166 // use info from class file header
167 if (classFile.isClass() && classFile.super_class != 0 ) {
168 String sn = getJavaSuperclassName(cf);
169 print(" extends ");
170 print(sn);
171 }
172 for (int i = 0; i < classFile.interfaces.length; i++) {
173 print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ",");
174 print(getJavaInterfaceName(classFile, i));
175 }
176 } else {
177 try {
178 Type t = sigAttr.getParsedSignature().getType(constant_pool);
179 // The signature parser cannot disambiguate between a
180 // FieldType and a ClassSignatureType that only contains a superclass type.
181 if (t instanceof Type.ClassSigType)
182 print(getJavaName(t.toString()));
183 else {
184 print(" extends ");
185 print(getJavaName(t.toString()));
186 }
187 } catch (ConstantPoolException e) {
188 print(report(e));
189 }
190 }
192 if (options.verbose) {
193 println();
194 indent(+1);
195 attrWriter.write(cf, cf.attributes, constant_pool);
196 println("minor version: " + cf.minor_version);
197 println("major version: " + cf.major_version);
198 if (!options.compat)
199 writeList("flags: ", flags.getClassFlags(), NEWLINE);
200 indent(-1);
201 constantWriter.writeConstantPool();
202 } else {
203 print(" ");
204 }
206 println("{");
207 indent(+1);
208 writeFields();
209 writeMethods();
210 indent(-1);
211 println("}");
212 }
214 protected void writeFields() {
215 for (Field f: classFile.fields) {
216 writeField(f);
217 }
218 }
220 protected void writeField(Field f) {
221 if (!options.checkAccess(f.access_flags))
222 return;
224 AccessFlags flags = f.access_flags;
225 writeModifiers(flags.getFieldModifiers());
226 Signature_attribute sigAttr = getSignature(f.attributes);
227 if (sigAttr == null)
228 print(getJavaFieldType(f.descriptor));
229 else {
230 try {
231 Type t = sigAttr.getParsedSignature().getType(constant_pool);
232 print(getJavaName(t.toString()));
233 } catch (ConstantPoolException e) {
234 // report error?
235 // fall back on non-generic descriptor
236 print(getJavaFieldType(f.descriptor));
237 }
238 }
239 print(" ");
240 print(getFieldName(f));
241 if (options.showConstants && !options.compat) { // BUG 4111861 print static final field contents
242 Attribute a = f.attributes.get(Attribute.ConstantValue);
243 if (a instanceof ConstantValue_attribute) {
244 print(" = ");
245 ConstantValue_attribute cv = (ConstantValue_attribute) a;
246 print(getConstantValue(f.descriptor, cv.constantvalue_index));
247 }
248 }
249 print(";");
250 println();
252 indent(+1);
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 indent(-1);
268 if (options.showDisassembled || options.showLineAndLocalVariableTables)
269 println();
270 }
272 protected void writeMethods() {
273 for (Method m: classFile.methods)
274 writeMethod(m);
275 setPendingNewline(false);
276 }
278 protected void writeMethod(Method m) {
279 if (!options.checkAccess(m.access_flags))
280 return;
282 method = m;
284 AccessFlags flags = m.access_flags;
286 Descriptor d;
287 Type.MethodType methodType;
288 List<? extends Type> methodExceptions;
290 Signature_attribute sigAttr = getSignature(m.attributes);
291 if (sigAttr == null) {
292 d = m.descriptor;
293 methodType = null;
294 methodExceptions = null;
295 } else {
296 Signature methodSig = sigAttr.getParsedSignature();
297 d = methodSig;
298 try {
299 methodType = (Type.MethodType) methodSig.getType(constant_pool);
300 methodExceptions = methodType.throwsTypes;
301 if (methodExceptions != null && methodExceptions.size() == 0)
302 methodExceptions = null;
303 } catch (ConstantPoolException e) {
304 // report error?
305 // fall back on standard descriptor
306 methodType = null;
307 methodExceptions = null;
308 }
309 }
311 writeModifiers(flags.getMethodModifiers());
312 if (methodType != null) {
313 writeListIfNotEmpty("<", methodType.typeParamTypes, "> ");
314 }
315 if (getName(m).equals("<init>")) {
316 print(getJavaName(classFile));
317 print(getJavaParameterTypes(d, flags));
318 } else if (getName(m).equals("<clinit>")) {
319 print("{}");
320 } else {
321 print(getJavaReturnType(d));
322 print(" ");
323 print(getName(m));
324 print(getJavaParameterTypes(d, flags));
325 }
327 Attribute e_attr = m.attributes.get(Attribute.Exceptions);
328 if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
329 if (e_attr instanceof Exceptions_attribute) {
330 Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
331 print(" throws ");
332 if (methodExceptions != null) { // use generic list if available
333 writeList("", methodExceptions, "");
334 } else {
335 for (int i = 0; i < exceptions.number_of_exceptions; i++) {
336 if (i > 0)
337 print(", ");
338 print(getJavaException(exceptions, i));
339 }
340 }
341 } else {
342 report("Unexpected or invalid value for Exceptions attribute");
343 }
344 }
346 println(";");
348 indent(+1);
350 if (options.showInternalSignatures) {
351 println("Signature: " + getValue(m.descriptor));
352 }
354 if (options.verbose && !options.compat) {
355 writeList("flags: ", flags.getMethodFlags(), NEWLINE);
356 }
358 Code_attribute code = null;
359 Attribute c_attr = m.attributes.get(Attribute.Code);
360 if (c_attr != null) {
361 if (c_attr instanceof Code_attribute)
362 code = (Code_attribute) c_attr;
363 else
364 report("Unexpected or invalid value for Code attribute");
365 }
367 if (options.showDisassembled && !options.showAllAttrs) {
368 if (code != null) {
369 println("Code:");
370 codeWriter.writeInstrs(code);
371 codeWriter.writeExceptionTable(code);
372 }
373 }
375 if (options.showLineAndLocalVariableTables) {
376 if (code != null) {
377 attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
378 attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
379 }
380 }
382 if (options.showAllAttrs) {
383 Attribute[] attrs = m.attributes.attrs;
384 for (Attribute attr: attrs)
385 attrWriter.write(m, attr, constant_pool);
386 }
388 indent(-1);
390 // set pendingNewline to write a newline before the next method (if any)
391 // if a separator is desired
392 setPendingNewline(
393 options.showDisassembled ||
394 options.showAllAttrs ||
395 options.showInternalSignatures ||
396 options.showLineAndLocalVariableTables ||
397 options.verbose);
398 }
400 void writeModifiers(Collection<String> items) {
401 for (Object item: items) {
402 print(item);
403 print(" ");
404 }
405 }
407 void writeList(String prefix, Collection<?> items, String suffix) {
408 print(prefix);
409 String sep = "";
410 for (Object item: items) {
411 print(sep);
412 print(item);
413 sep = ", ";
414 }
415 print(suffix);
416 }
418 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
419 if (items != null && items.size() > 0)
420 writeList(prefix, items, suffix);
421 }
423 Signature_attribute getSignature(Attributes attributes) {
424 if (options.compat) // javap does not recognize recent attributes
425 return null;
426 return (Signature_attribute) attributes.get(Attribute.Signature);
427 }
429 String adjustVarargs(AccessFlags flags, String params) {
430 if (flags.is(ACC_VARARGS) && !options.compat) {
431 int i = params.lastIndexOf("[]");
432 if (i > 0)
433 return params.substring(0, i) + "..." + params.substring(i+2);
434 }
436 return params;
437 }
439 String getJavaName(ClassFile cf) {
440 try {
441 return getJavaName(cf.getName());
442 } catch (ConstantPoolException e) {
443 return report(e);
444 }
445 }
447 String getJavaSuperclassName(ClassFile cf) {
448 try {
449 return getJavaName(cf.getSuperclassName());
450 } catch (ConstantPoolException e) {
451 return report(e);
452 }
453 }
455 String getJavaInterfaceName(ClassFile cf, int index) {
456 try {
457 return getJavaName(cf.getInterfaceName(index));
458 } catch (ConstantPoolException e) {
459 return report(e);
460 }
461 }
463 String getJavaFieldType(Descriptor d) {
464 try {
465 return getJavaName(d.getFieldType(constant_pool));
466 } catch (ConstantPoolException e) {
467 return report(e);
468 } catch (DescriptorException e) {
469 return report(e);
470 }
471 }
473 String getJavaReturnType(Descriptor d) {
474 try {
475 return getJavaName(d.getReturnType(constant_pool));
476 } catch (ConstantPoolException e) {
477 return report(e);
478 } catch (DescriptorException e) {
479 return report(e);
480 }
481 }
483 String getJavaParameterTypes(Descriptor d, AccessFlags flags) {
484 try {
485 return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool)));
486 } catch (ConstantPoolException e) {
487 return report(e);
488 } catch (DescriptorException e) {
489 return report(e);
490 }
491 }
493 String getJavaException(Exceptions_attribute attr, int index) {
494 try {
495 return getJavaName(attr.getException(index, constant_pool));
496 } catch (ConstantPoolException e) {
497 return report(e);
498 }
499 }
501 String getValue(Descriptor d) {
502 try {
503 return d.getValue(constant_pool);
504 } catch (ConstantPoolException e) {
505 return report(e);
506 }
507 }
509 String getFieldName(Field f) {
510 try {
511 return f.getName(constant_pool);
512 } catch (ConstantPoolException e) {
513 return report(e);
514 }
515 }
517 String getName(Method m) {
518 try {
519 return m.getName(constant_pool);
520 } catch (ConstantPoolException e) {
521 return report(e);
522 }
523 }
525 static String getJavaName(String name) {
526 return name.replace('/', '.');
527 }
529 String getSourceFile(SourceFile_attribute attr) {
530 try {
531 return attr.getSourceFile(constant_pool);
532 } catch (ConstantPoolException e) {
533 return report(e);
534 }
535 }
537 /**
538 * Get the value of an entry in the constant pool as a Java constant.
539 * Characters and booleans are represented by CONSTANT_Intgere entries.
540 * Character and string values are processed to escape characters outside
541 * the basic printable ASCII set.
542 * @param d the descriptor, giving the expected type of the constant
543 * @param index the index of the value in the constant pool
544 * @return a printable string containing the value of the constant.
545 */
546 String getConstantValue(Descriptor d, int index) {
547 try {
548 ConstantPool.CPInfo cpInfo = constant_pool.get(index);
550 switch (cpInfo.getTag()) {
551 case ConstantPool.CONSTANT_Integer: {
552 ConstantPool.CONSTANT_Integer_info info =
553 (ConstantPool.CONSTANT_Integer_info) cpInfo;
554 String t = d.getValue(constant_pool);
555 if (t.equals("C")) { // character
556 return getConstantCharValue((char) info.value);
557 } else if (t.equals("Z")) { // boolean
558 return String.valueOf(info.value == 1);
559 } else { // other: assume integer
560 return String.valueOf(info.value);
561 }
562 }
564 case ConstantPool.CONSTANT_String: {
565 ConstantPool.CONSTANT_String_info info =
566 (ConstantPool.CONSTANT_String_info) cpInfo;
567 return getConstantStringValue(info.getString());
568 }
570 default:
571 return constantWriter.stringValue(cpInfo);
572 }
573 } catch (ConstantPoolException e) {
574 return "#" + index;
575 }
576 }
578 private String getConstantCharValue(char c) {
579 StringBuilder sb = new StringBuilder();
580 sb.append('\'');
581 sb.append(esc(c, '\''));
582 sb.append('\'');
583 return sb.toString();
584 }
586 private String getConstantStringValue(String s) {
587 StringBuilder sb = new StringBuilder();
588 sb.append("\"");
589 for (int i = 0; i < s.length(); i++) {
590 sb.append(esc(s.charAt(i), '"'));
591 }
592 sb.append("\"");
593 return sb.toString();
594 }
596 private String esc(char c, char quote) {
597 if (32 <= c && c <= 126 && c != quote)
598 return String.valueOf(c);
599 else switch (c) {
600 case '\b': return "\\b";
601 case '\n': return "\\n";
602 case '\t': return "\\t";
603 case '\f': return "\\f";
604 case '\r': return "\\r";
605 case '\\': return "\\\\";
606 case '\'': return "\\'";
607 case '\"': return "\\\"";
608 default: return String.format("\\u%04x", (int) c);
609 }
610 }
612 private Options options;
613 private AttributeWriter attrWriter;
614 private CodeWriter codeWriter;
615 private ConstantWriter constantWriter;
616 private ClassFile classFile;
617 private URI uri;
618 private long lastModified;
619 private String digestName;
620 private byte[] digest;
621 private int size;
622 private ConstantPool constant_pool;
623 private Method method;
624 private static final String NEWLINE = System.getProperty("line.separator", "\n");
625 }