Tue, 16 Sep 2008 18:35:18 -0700
6574134: Allow for alternative implementation of Name Table with garbage collection of name bytes
Reviewed-by: darcy, 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.util.Collection;
30 import java.util.List;
32 import com.sun.tools.classfile.AccessFlags;
33 import com.sun.tools.classfile.Attribute;
34 import com.sun.tools.classfile.Attributes;
35 import com.sun.tools.classfile.ClassFile;
36 import com.sun.tools.classfile.Code_attribute;
37 import com.sun.tools.classfile.ConstantPool;
38 import com.sun.tools.classfile.ConstantPoolException;
39 import com.sun.tools.classfile.ConstantValue_attribute;
40 import com.sun.tools.classfile.Descriptor;
41 import com.sun.tools.classfile.DescriptorException;
42 import com.sun.tools.classfile.Exceptions_attribute;
43 import com.sun.tools.classfile.Field;
44 import com.sun.tools.classfile.Method;
45 import com.sun.tools.classfile.Signature;
46 import com.sun.tools.classfile.Signature_attribute;
47 import com.sun.tools.classfile.SourceFile_attribute;
48 import com.sun.tools.classfile.Type;
50 import java.text.DateFormat;
51 import java.util.Date;
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 ClassFile getClassFile() {
97 return classFile;
98 }
100 Method getMethod() {
101 return method;
102 }
104 public void write(ClassFile cf) {
105 classFile = cf;
106 constant_pool = classFile.constant_pool;
108 if ((options.sysInfo || options.verbose) && !options.compat) {
109 if (uri != null) {
110 if (uri.getScheme().equals("file"))
111 println("Classfile " + uri.getPath());
112 else
113 println("Classfile " + uri);
114 }
115 if (lastModified != -1) {
116 Date lm = new Date(lastModified);
117 DateFormat df = DateFormat.getDateInstance();
118 if (size > 0) {
119 println("Last modified " + df.format(lm) + "; size " + size + " bytes");
120 } else {
121 println("Last modified " + df.format(lm));
122 }
123 } else if (size > 0) {
124 println("Size " + size + " bytes");
125 }
126 if (digestName != null && digest != null) {
127 StringBuilder sb = new StringBuilder();
128 for (byte b: digest)
129 sb.append(String.format("%02x", b));
130 println(digestName + " checksum " + sb);
131 }
132 }
134 Attribute sfa = cf.getAttribute(Attribute.SourceFile);
135 if (sfa instanceof SourceFile_attribute) {
136 println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\"");
137 }
139 String name = getJavaName(classFile);
140 AccessFlags flags = cf.access_flags;
142 writeModifiers(flags.getClassModifiers());
144 if (classFile.isClass())
145 print("class ");
146 else if (classFile.isInterface())
147 print("interface ");
149 print(name);
151 Signature_attribute sigAttr = getSignature(cf.attributes);
152 if (sigAttr == null) {
153 // use info from class file header
154 if (classFile.isClass() && classFile.super_class != 0 ) {
155 String sn = getJavaSuperclassName(cf);
156 print(" extends ");
157 print(sn);
158 }
159 for (int i = 0; i < classFile.interfaces.length; i++) {
160 print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ",");
161 print(getJavaInterfaceName(classFile, i));
162 }
163 } else {
164 try {
165 Type t = sigAttr.getParsedSignature().getType(constant_pool);
166 // The signature parser cannot disambiguate between a
167 // FieldType and a ClassSignatureType that only contains a superclass type.
168 if (t instanceof Type.ClassSigType)
169 print(t);
170 else {
171 print(" extends ");
172 print(t);
173 }
174 } catch (ConstantPoolException e) {
175 print(report(e));
176 }
177 }
179 if (options.verbose) {
180 println();
181 attrWriter.write(cf, cf.attributes, constant_pool);
182 println(" minor version: " + cf.minor_version);
183 println(" major version: " + cf.major_version);
184 if (!options.compat)
185 writeList(" flags: ", flags.getClassFlags(), NEWLINE);
186 constantWriter.writeConstantPool();
187 println();
188 } else {
189 if (!options.compat)
190 print(" ");
191 }
193 println("{");
194 writeFields();
195 writeMethods();
196 println("}");
197 println();
198 }
200 void writeFields() {
201 for (Field f: classFile.fields) {
202 writeField(f);
203 }
204 }
206 void writeField(Field f) {
207 if (!options.checkAccess(f.access_flags))
208 return;
210 if (!(options.showLineAndLocalVariableTables
211 || options.showDisassembled
212 || options.verbose
213 || options.showInternalSignatures
214 || options.showAllAttrs)) {
215 print(" ");
216 }
218 AccessFlags flags = f.access_flags;
219 writeModifiers(flags.getFieldModifiers());
220 Signature_attribute sigAttr = getSignature(f.attributes);
221 if (sigAttr == null)
222 print(getFieldType(f.descriptor));
223 else {
224 try {
225 Type t = sigAttr.getParsedSignature().getType(constant_pool);
226 print(t);
227 } catch (ConstantPoolException e) {
228 // report error?
229 // fall back on non-generic descriptor
230 print(getFieldType(f.descriptor));
231 }
232 }
233 print(" ");
234 print(getFieldName(f));
235 if (options.showConstants && !options.compat) { // BUG 4111861 print static final field contents
236 Attribute a = f.attributes.get(Attribute.ConstantValue);
237 if (a instanceof ConstantValue_attribute) {
238 print(" = ");
239 ConstantValue_attribute cv = (ConstantValue_attribute) a;
240 print(getConstantValue(f.descriptor, cv.constantvalue_index));
241 }
242 }
243 print(";");
244 println();
246 if (options.showInternalSignatures)
247 println(" Signature: " + getValue(f.descriptor));
249 if (options.verbose && !options.compat)
250 writeList(" flags: ", flags.getFieldFlags(), NEWLINE);
252 if (options.showAllAttrs) {
253 for (Attribute attr: f.attributes)
254 attrWriter.write(f, attr, constant_pool);
255 println();
256 }
258 if (options.showDisassembled || options.showLineAndLocalVariableTables)
259 println();
260 }
262 void writeMethods() {
263 for (Method m: classFile.methods)
264 writeMethod(m);
265 }
267 void writeMethod(Method m) {
268 if (!options.checkAccess(m.access_flags))
269 return;
271 method = m;
273 if (!(options.showLineAndLocalVariableTables
274 || options.showDisassembled
275 || options.verbose
276 || options.showInternalSignatures
277 || options.showAllAttrs)) {
278 print(" ");
279 }
281 AccessFlags flags = m.access_flags;
283 Descriptor d;
284 Type.MethodType methodType;
285 List<? extends Type> methodExceptions;
287 Signature_attribute sigAttr = getSignature(m.attributes);
288 if (sigAttr == null) {
289 d = m.descriptor;
290 methodType = null;
291 methodExceptions = null;
292 } else {
293 Signature methodSig = sigAttr.getParsedSignature();
294 d = methodSig;
295 try {
296 methodType = (Type.MethodType) methodSig.getType(constant_pool);
297 methodExceptions = methodType.throwsTypes;
298 if (methodExceptions != null && methodExceptions.size() == 0)
299 methodExceptions = null;
300 } catch (ConstantPoolException e) {
301 // report error?
302 // fall back on standard descriptor
303 methodType = null;
304 methodExceptions = null;
305 }
306 }
308 writeModifiers(flags.getMethodModifiers());
309 if (methodType != null) {
310 writeListIfNotEmpty("<", methodType.typeArgTypes, "> ");
311 }
312 if (getName(m).equals("<init>")) {
313 print(getJavaName(classFile));
314 print(getParameterTypes(d, flags));
315 } else if (getName(m).equals("<clinit>")) {
316 print("{}");
317 } else {
318 print(getReturnType(d));
319 print(" ");
320 print(getName(m));
321 print(getParameterTypes(d, flags));
322 }
324 Attribute e_attr = m.attributes.get(Attribute.Exceptions);
325 if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
326 if (e_attr instanceof Exceptions_attribute) {
327 Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
328 if (options.compat) { // Bug XXXXXXX whitespace
329 if (!(options.showLineAndLocalVariableTables
330 || options.showDisassembled
331 || options.verbose
332 || options.showInternalSignatures
333 || options.showAllAttrs)) {
334 print(" ");
335 }
336 print(" ");
337 }
338 print(" throws ");
339 if (methodExceptions != null) { // use generic list if available
340 writeList("", methodExceptions, "");
341 } else {
342 for (int i = 0; i < exceptions.number_of_exceptions; i++) {
343 if (i > 0)
344 print(", ");
345 print(getJavaException(exceptions, i));
346 }
347 }
348 } else {
349 report("Unexpected or invalid value for Exceptions attribute");
350 }
351 }
353 print(";");
354 println();
356 if (options.showInternalSignatures)
357 println(" Signature: " + getValue(m.descriptor));
359 if (options.verbose && !options.compat)
360 writeList(" flags: ", flags.getMethodFlags(), NEWLINE);
362 Code_attribute code = null;
363 Attribute c_attr = m.attributes.get(Attribute.Code);
364 if (c_attr != null) {
365 if (c_attr instanceof Code_attribute)
366 code = (Code_attribute) c_attr;
367 else
368 report("Unexpected or invalid value for Code attribute");
369 }
371 if (options.showDisassembled && !options.showAllAttrs) {
372 if (code != null) {
373 println(" Code:");
374 codeWriter.writeInstrs(code);
375 codeWriter.writeExceptionTable(code);
376 }
377 println();
378 }
380 if (options.showLineAndLocalVariableTables) {
381 if (code != null)
382 attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
383 println();
384 if (code != null)
385 attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
386 println();
387 println();
388 }
390 if (options.showAllAttrs) {
391 Attribute[] attrs = m.attributes.attrs;
392 for (Attribute attr: attrs)
393 attrWriter.write(m, attr, constant_pool);
395 // // the following condition is to mimic old javap
396 // if (!(attrs.length > 0 &&
397 // attrs[attrs.length - 1] instanceof Exceptions_attribute))
398 println();
399 }
400 }
402 void writeModifiers(Collection<String> items) {
403 for (Object item: items) {
404 print(item);
405 print(" ");
406 }
407 }
409 void writeList(String prefix, Collection<?> items, String suffix) {
410 print(prefix);
411 String sep = "";
412 for (Object item: items) {
413 print(sep);
414 print(item);
415 sep = ", ";
416 }
417 print(suffix);
418 }
420 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
421 if (items != null && items.size() > 0)
422 writeList(prefix, items, suffix);
423 }
425 Signature_attribute getSignature(Attributes attributes) {
426 if (options.compat) // javap does not recognize recent attributes
427 return null;
428 return (Signature_attribute) attributes.get(Attribute.Signature);
429 }
431 String adjustVarargs(AccessFlags flags, String params) {
432 if (flags.is(ACC_VARARGS) && !options.compat) {
433 int i = params.lastIndexOf("[]");
434 if (i > 0)
435 return params.substring(0, i) + "..." + params.substring(i+2);
436 }
438 return params;
439 }
441 String getJavaName(ClassFile cf) {
442 try {
443 return getJavaName(cf.getName());
444 } catch (ConstantPoolException e) {
445 return report(e);
446 }
447 }
449 String getJavaSuperclassName(ClassFile cf) {
450 try {
451 return getJavaName(cf.getSuperclassName());
452 } catch (ConstantPoolException e) {
453 return report(e);
454 }
455 }
457 String getJavaInterfaceName(ClassFile cf, int index) {
458 try {
459 return getJavaName(cf.getInterfaceName(index));
460 } catch (ConstantPoolException e) {
461 return report(e);
462 }
463 }
465 String getFieldType(Descriptor d) {
466 try {
467 return d.getFieldType(constant_pool);
468 } catch (ConstantPoolException e) {
469 return report(e);
470 } catch (DescriptorException e) {
471 return report(e);
472 }
473 }
475 String getReturnType(Descriptor d) {
476 try {
477 return d.getReturnType(constant_pool);
478 } catch (ConstantPoolException e) {
479 return report(e);
480 } catch (DescriptorException e) {
481 return report(e);
482 }
483 }
485 String getParameterTypes(Descriptor d, AccessFlags flags) {
486 try {
487 return adjustVarargs(flags, d.getParameterTypes(constant_pool));
488 } catch (ConstantPoolException e) {
489 return report(e);
490 } catch (DescriptorException e) {
491 return report(e);
492 }
493 }
495 String getJavaException(Exceptions_attribute attr, int index) {
496 try {
497 return getJavaName(attr.getException(index, constant_pool));
498 } catch (ConstantPoolException e) {
499 return report(e);
500 }
501 }
503 String getValue(Descriptor d) {
504 try {
505 return d.getValue(constant_pool);
506 } catch (ConstantPoolException e) {
507 return report(e);
508 }
509 }
511 String getFieldName(Field f) {
512 try {
513 return f.getName(constant_pool);
514 } catch (ConstantPoolException e) {
515 return report(e);
516 }
517 }
519 String getName(Method m) {
520 try {
521 return m.getName(constant_pool);
522 } catch (ConstantPoolException e) {
523 return report(e);
524 }
525 }
527 static String getJavaName(String name) {
528 return name.replace('/', '.');
529 }
531 String getSourceFile(SourceFile_attribute attr) {
532 try {
533 return attr.getSourceFile(constant_pool);
534 } catch (ConstantPoolException e) {
535 return report(e);
536 }
537 }
539 /**
540 * Get the value of an entry in the constant pool as a Java constant.
541 * Characters and booleans are represented by CONSTANT_Intgere entries.
542 * Character and string values are processed to escape characters outside
543 * the basic printable ASCII set.
544 * @param d the descriptor, giving the expected type of the constant
545 * @param index the index of the value in the constant pool
546 * @return a printable string containing the value of the constant.
547 */
548 String getConstantValue(Descriptor d, int index) {
549 try {
550 ConstantPool.CPInfo cpInfo = constant_pool.get(index);
552 switch (cpInfo.getTag()) {
553 case ConstantPool.CONSTANT_Integer: {
554 ConstantPool.CONSTANT_Integer_info info =
555 (ConstantPool.CONSTANT_Integer_info) cpInfo;
556 String t = d.getValue(constant_pool);
557 if (t.equals("C")) { // character
558 return getConstantCharValue((char) info.value);
559 } else if (t.equals("Z")) { // boolean
560 return String.valueOf(info.value == 1);
561 } else { // other: assume integer
562 return String.valueOf(info.value);
563 }
564 }
566 case ConstantPool.CONSTANT_String: {
567 ConstantPool.CONSTANT_String_info info =
568 (ConstantPool.CONSTANT_String_info) cpInfo;
569 return getConstantStringValue(info.getString());
570 }
572 default:
573 return constantWriter.stringValue(cpInfo);
574 }
575 } catch (ConstantPoolException e) {
576 return "#" + index;
577 }
578 }
580 private String getConstantCharValue(char c) {
581 StringBuilder sb = new StringBuilder();
582 sb.append('\'');
583 sb.append(esc(c, '\''));
584 sb.append('\'');
585 return sb.toString();
586 }
588 private String getConstantStringValue(String s) {
589 StringBuilder sb = new StringBuilder();
590 sb.append("\"");
591 for (int i = 0; i < s.length(); i++) {
592 sb.append(esc(s.charAt(i), '"'));
593 }
594 sb.append("\"");
595 return sb.toString();
596 }
598 private String esc(char c, char quote) {
599 if (32 <= c && c <= 126 && c != quote)
600 return String.valueOf(c);
601 else switch (c) {
602 case '\b': return "\\b";
603 case '\n': return "\\n";
604 case '\t': return "\\t";
605 case '\f': return "\\f";
606 case '\r': return "\\r";
607 case '\\': return "\\\\";
608 case '\'': return "\\'";
609 case '\"': return "\\\"";
610 default: return String.format("\\u%04x", (int) c);
611 }
612 }
614 private Options options;
615 private AttributeWriter attrWriter;
616 private CodeWriter codeWriter;
617 private ConstantWriter constantWriter;
618 private ClassFile classFile;
619 private URI uri;
620 private long lastModified;
621 private String digestName;
622 private byte[] digest;
623 private int size;
624 private ConstantPool constant_pool;
625 private Method method;
626 private static final String NEWLINE = System.getProperty("line.separator", "\n");
627 }