001 /*
002 * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package com.sun.tools.javap;
027
028 import java.net.URI;
029 import java.util.Collection;
030 import java.util.List;
031
032 import com.sun.tools.classfile.AccessFlags;
033 import com.sun.tools.classfile.Attribute;
034 import com.sun.tools.classfile.Attributes;
035 import com.sun.tools.classfile.ClassFile;
036 import com.sun.tools.classfile.Code_attribute;
037 import com.sun.tools.classfile.ConstantPool;
038 import com.sun.tools.classfile.ConstantPoolException;
039 import com.sun.tools.classfile.ConstantValue_attribute;
040 import com.sun.tools.classfile.Descriptor;
041 import com.sun.tools.classfile.DescriptorException;
042 import com.sun.tools.classfile.Exceptions_attribute;
043 import com.sun.tools.classfile.Field;
044 import com.sun.tools.classfile.Method;
045 import com.sun.tools.classfile.Signature;
046 import com.sun.tools.classfile.Signature_attribute;
047 import com.sun.tools.classfile.SourceFile_attribute;
048 import com.sun.tools.classfile.Type;
049
050 import java.text.DateFormat;
051 import java.util.Date;
052 import static com.sun.tools.classfile.AccessFlags.*;
053
054 /*
055 * The main javap class to write the contents of a class file as text.
056 *
057 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
058 * you write code that depends on this, you do so at your own risk.
059 * This code and its internal interfaces are subject to change or
060 * deletion without notice.</b>
061 */
062 public class ClassWriter extends BasicWriter {
063 static ClassWriter instance(Context context) {
064 ClassWriter instance = context.get(ClassWriter.class);
065 if (instance == null)
066 instance = new ClassWriter(context);
067 return instance;
068 }
069
070 protected ClassWriter(Context context) {
071 super(context);
072 context.put(ClassWriter.class, this);
073 options = Options.instance(context);
074 attrWriter = AttributeWriter.instance(context);
075 codeWriter = CodeWriter.instance(context);
076 constantWriter = ConstantWriter.instance(context);
077 }
078
079 void setDigest(String name, byte[] digest) {
080 this.digestName = name;
081 this.digest = digest;
082 }
083
084 void setFile(URI uri) {
085 this.uri = uri;
086 }
087
088 void setFileSize(int size) {
089 this.size = size;
090 }
091
092 void setLastModified(long lastModified) {
093 this.lastModified = lastModified;
094 }
095
096 ClassFile getClassFile() {
097 return classFile;
098 }
099
100 Method getMethod() {
101 return method;
102 }
103
104 public void write(ClassFile cf) {
105 classFile = cf;
106 constant_pool = classFile.constant_pool;
107
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 }
133
134 Attribute sfa = cf.getAttribute(Attribute.SourceFile);
135 if (sfa instanceof SourceFile_attribute) {
136 println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\"");
137 }
138
139 String name = getJavaName(classFile);
140 AccessFlags flags = cf.access_flags;
141
142 writeModifiers(flags.getClassModifiers());
143
144 if (classFile.isClass())
145 print("class ");
146 else if (classFile.isInterface())
147 print("interface ");
148
149 print(name);
150
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 }
178
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 }
192
193 println("{");
194 writeFields();
195 writeMethods();
196 println("}");
197 println();
198 }
199
200 void writeFields() {
201 for (Field f: classFile.fields) {
202 writeField(f);
203 }
204 }
205
206 void writeField(Field f) {
207 if (!options.checkAccess(f.access_flags))
208 return;
209
210 if (!(options.showLineAndLocalVariableTables
211 || options.showDisassembled
212 || options.verbose
213 || options.showInternalSignatures
214 || options.showAllAttrs)) {
215 print(" ");
216 }
217
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();
245
246 if (options.showInternalSignatures)
247 println(" Signature: " + getValue(f.descriptor));
248
249 if (options.verbose && !options.compat)
250 writeList(" flags: ", flags.getFieldFlags(), NEWLINE);
251
252 if (options.showAllAttrs) {
253 for (Attribute attr: f.attributes)
254 attrWriter.write(f, attr, constant_pool);
255 println();
256 }
257
258 if (options.showDisassembled || options.showLineAndLocalVariableTables)
259 println();
260 }
261
262 void writeMethods() {
263 for (Method m: classFile.methods)
264 writeMethod(m);
265 }
266
267 void writeMethod(Method m) {
268 if (!options.checkAccess(m.access_flags))
269 return;
270
271 method = m;
272
273 if (!(options.showLineAndLocalVariableTables
274 || options.showDisassembled
275 || options.verbose
276 || options.showInternalSignatures
277 || options.showAllAttrs)) {
278 print(" ");
279 }
280
281 AccessFlags flags = m.access_flags;
282
283 Descriptor d;
284 Type.MethodType methodType;
285 List<? extends Type> methodExceptions;
286
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 }
307
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 }
323
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 }
352
353 print(";");
354 println();
355
356 if (options.showInternalSignatures)
357 println(" Signature: " + getValue(m.descriptor));
358
359 if (options.verbose && !options.compat)
360 writeList(" flags: ", flags.getMethodFlags(), NEWLINE);
361
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 }
370
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 }
379
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 }
389
390 if (options.showAllAttrs) {
391 Attribute[] attrs = m.attributes.attrs;
392 for (Attribute attr: attrs)
393 attrWriter.write(m, attr, constant_pool);
394
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 }
401
402 void writeModifiers(Collection<String> items) {
403 for (Object item: items) {
404 print(item);
405 print(" ");
406 }
407 }
408
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 }
419
420 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
421 if (items != null && items.size() > 0)
422 writeList(prefix, items, suffix);
423 }
424
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 }
430
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 }
437
438 return params;
439 }
440
441 String getJavaName(ClassFile cf) {
442 try {
443 return getJavaName(cf.getName());
444 } catch (ConstantPoolException e) {
445 return report(e);
446 }
447 }
448
449 String getJavaSuperclassName(ClassFile cf) {
450 try {
451 return getJavaName(cf.getSuperclassName());
452 } catch (ConstantPoolException e) {
453 return report(e);
454 }
455 }
456
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 }
464
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 }
474
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 }
484
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 }
494
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 }
502
503 String getValue(Descriptor d) {
504 try {
505 return d.getValue(constant_pool);
506 } catch (ConstantPoolException e) {
507 return report(e);
508 }
509 }
510
511 String getFieldName(Field f) {
512 try {
513 return f.getName(constant_pool);
514 } catch (ConstantPoolException e) {
515 return report(e);
516 }
517 }
518
519 String getName(Method m) {
520 try {
521 return m.getName(constant_pool);
522 } catch (ConstantPoolException e) {
523 return report(e);
524 }
525 }
526
527 static String getJavaName(String name) {
528 return name.replace('/', '.');
529 }
530
531 String getSourceFile(SourceFile_attribute attr) {
532 try {
533 return attr.getSourceFile(constant_pool);
534 } catch (ConstantPoolException e) {
535 return report(e);
536 }
537 }
538
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);
551
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 }
565
566 case ConstantPool.CONSTANT_String: {
567 ConstantPool.CONSTANT_String_info info =
568 (ConstantPool.CONSTANT_String_info) cpInfo;
569 return getConstantStringValue(info.getString());
570 }
571
572 default:
573 return constantWriter.stringValue(cpInfo);
574 }
575 } catch (ConstantPoolException e) {
576 return "#" + index;
577 }
578 }
579
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 }
587
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 }
597
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 }
613
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 }