Fri, 31 Aug 2012 10:37:46 +0100
7151010: Add compiler support for repeating annotations
Reviewed-by: jjg, mcimadamore
1 /*
2 * Copyright (c) 2007, 2011, 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(), NEWLINE);
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 public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) {
236 sb.append(getJavaName(type.name));
237 return sb;
238 }
240 public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) {
241 append(sb, type.elemType);
242 sb.append("[]");
243 return sb;
244 }
246 public StringBuilder visitMethodType(MethodType type, StringBuilder sb) {
247 appendIfNotEmpty(sb, "<", type.typeParamTypes, "> ");
248 append(sb, type.returnType);
249 append(sb, " (", type.paramTypes, ")");
250 appendIfNotEmpty(sb, " throws ", type.throwsTypes, "");
251 return sb;
252 }
254 public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) {
255 appendIfNotEmpty(sb, "<", type.typeParamTypes, ">");
256 if (isInterface) {
257 appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, "");
258 } else {
259 if (type.superclassType != null
260 && (options.verbose || !type.superclassType.isObject())) {
261 sb.append(" extends ");
262 append(sb, type.superclassType);
263 }
264 appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, "");
265 }
266 return sb;
267 }
269 public StringBuilder visitClassType(ClassType type, StringBuilder sb) {
270 if (type.outerType != null) {
271 append(sb, type.outerType);
272 sb.append(".");
273 }
274 sb.append(getJavaName(type.name));
275 appendIfNotEmpty(sb, "<", type.typeArgs, ">");
276 return sb;
277 }
279 public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) {
280 sb.append(type.name);
281 String sep = " extends ";
282 if (type.classBound != null
283 && (options.verbose || !type.classBound.isObject())) {
284 sb.append(sep);
285 append(sb, type.classBound);
286 sep = " & ";
287 }
288 if (type.interfaceBounds != null) {
289 for (Type bound: type.interfaceBounds) {
290 sb.append(sep);
291 append(sb, bound);
292 sep = " & ";
293 }
294 }
295 return sb;
296 }
298 public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) {
299 switch (type.kind) {
300 case UNBOUNDED:
301 sb.append("?");
302 break;
303 case EXTENDS:
304 sb.append("? extends ");
305 append(sb, type.boundType);
306 break;
307 case SUPER:
308 sb.append("? super ");
309 append(sb, type.boundType);
310 break;
311 default:
312 throw new AssertionError();
313 }
314 return sb;
315 }
317 private void append(StringBuilder sb, Type t) {
318 t.accept(this, sb);
319 }
321 private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
322 sb.append(prefix);
323 String sep = "";
324 for (Type t: list) {
325 sb.append(sep);
326 append(sb, t);
327 sep = ", ";
328 }
329 sb.append(suffix);
330 }
332 private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
333 if (!isEmpty(list))
334 append(sb, prefix, list, suffix);
335 }
337 private boolean isEmpty(List<? extends Type> list) {
338 return (list == null || list.isEmpty());
339 }
340 }
342 protected void writeFields() {
343 for (Field f: classFile.fields) {
344 writeField(f);
345 }
346 }
348 protected void writeField(Field f) {
349 if (!options.checkAccess(f.access_flags))
350 return;
352 AccessFlags flags = f.access_flags;
353 writeModifiers(flags.getFieldModifiers());
354 Signature_attribute sigAttr = getSignature(f.attributes);
355 if (sigAttr == null)
356 print(getJavaFieldType(f.descriptor));
357 else {
358 try {
359 Type t = sigAttr.getParsedSignature().getType(constant_pool);
360 print(getJavaName(t.toString()));
361 } catch (ConstantPoolException e) {
362 // report error?
363 // fall back on non-generic descriptor
364 print(getJavaFieldType(f.descriptor));
365 }
366 }
367 print(" ");
368 print(getFieldName(f));
369 if (options.showConstants && !options.compat) { // BUG 4111861 print static final field contents
370 Attribute a = f.attributes.get(Attribute.ConstantValue);
371 if (a instanceof ConstantValue_attribute) {
372 print(" = ");
373 ConstantValue_attribute cv = (ConstantValue_attribute) a;
374 print(getConstantValue(f.descriptor, cv.constantvalue_index));
375 }
376 }
377 print(";");
378 println();
380 indent(+1);
382 if (options.showInternalSignatures)
383 println("Signature: " + getValue(f.descriptor));
385 if (options.verbose && !options.compat)
386 writeList("flags: ", flags.getFieldFlags(), NEWLINE);
388 if (options.showAllAttrs) {
389 for (Attribute attr: f.attributes)
390 attrWriter.write(f, attr, constant_pool);
391 println();
392 }
394 indent(-1);
396 if (options.showDisassembled || options.showLineAndLocalVariableTables)
397 println();
398 }
400 protected void writeMethods() {
401 for (Method m: classFile.methods)
402 writeMethod(m);
403 setPendingNewline(false);
404 }
406 protected void writeMethod(Method m) {
407 if (!options.checkAccess(m.access_flags))
408 return;
410 method = m;
412 AccessFlags flags = m.access_flags;
414 Descriptor d;
415 Type.MethodType methodType;
416 List<? extends Type> methodExceptions;
418 Signature_attribute sigAttr = getSignature(m.attributes);
419 if (sigAttr == null) {
420 d = m.descriptor;
421 methodType = null;
422 methodExceptions = null;
423 } else {
424 Signature methodSig = sigAttr.getParsedSignature();
425 d = methodSig;
426 try {
427 methodType = (Type.MethodType) methodSig.getType(constant_pool);
428 methodExceptions = methodType.throwsTypes;
429 if (methodExceptions != null && methodExceptions.isEmpty())
430 methodExceptions = null;
431 } catch (ConstantPoolException e) {
432 // report error?
433 // fall back on standard descriptor
434 methodType = null;
435 methodExceptions = null;
436 }
437 }
439 writeModifiers(flags.getMethodModifiers());
440 if (methodType != null) {
441 writeListIfNotEmpty("<", methodType.typeParamTypes, "> ");
442 }
443 if (getName(m).equals("<init>")) {
444 print(getJavaName(classFile));
445 print(getJavaParameterTypes(d, flags));
446 } else if (getName(m).equals("<clinit>")) {
447 print("{}");
448 } else {
449 print(getJavaReturnType(d));
450 print(" ");
451 print(getName(m));
452 print(getJavaParameterTypes(d, flags));
453 }
455 Attribute e_attr = m.attributes.get(Attribute.Exceptions);
456 if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
457 if (e_attr instanceof Exceptions_attribute) {
458 Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
459 print(" throws ");
460 if (methodExceptions != null) { // use generic list if available
461 writeList("", methodExceptions, "");
462 } else {
463 for (int i = 0; i < exceptions.number_of_exceptions; i++) {
464 if (i > 0)
465 print(", ");
466 print(getJavaException(exceptions, i));
467 }
468 }
469 } else {
470 report("Unexpected or invalid value for Exceptions attribute");
471 }
472 }
474 println(";");
476 indent(+1);
478 if (options.showInternalSignatures) {
479 println("Signature: " + getValue(m.descriptor));
480 }
482 if (options.verbose && !options.compat) {
483 writeList("flags: ", flags.getMethodFlags(), NEWLINE);
484 }
486 Code_attribute code = null;
487 Attribute c_attr = m.attributes.get(Attribute.Code);
488 if (c_attr != null) {
489 if (c_attr instanceof Code_attribute)
490 code = (Code_attribute) c_attr;
491 else
492 report("Unexpected or invalid value for Code attribute");
493 }
495 if (options.showDisassembled && !options.showAllAttrs) {
496 if (code != null) {
497 println("Code:");
498 codeWriter.writeInstrs(code);
499 codeWriter.writeExceptionTable(code);
500 }
501 }
503 if (options.showLineAndLocalVariableTables) {
504 if (code != null) {
505 attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
506 attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
507 }
508 }
510 if (options.showAllAttrs) {
511 Attribute[] attrs = m.attributes.attrs;
512 for (Attribute attr: attrs)
513 attrWriter.write(m, attr, constant_pool);
514 }
516 indent(-1);
518 // set pendingNewline to write a newline before the next method (if any)
519 // if a separator is desired
520 setPendingNewline(
521 options.showDisassembled ||
522 options.showAllAttrs ||
523 options.showInternalSignatures ||
524 options.showLineAndLocalVariableTables ||
525 options.verbose);
526 }
528 void writeModifiers(Collection<String> items) {
529 for (Object item: items) {
530 print(item);
531 print(" ");
532 }
533 }
535 void writeList(String prefix, Collection<?> items, String suffix) {
536 print(prefix);
537 String sep = "";
538 for (Object item: items) {
539 print(sep);
540 print(item);
541 sep = ", ";
542 }
543 print(suffix);
544 }
546 void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
547 if (items != null && items.size() > 0)
548 writeList(prefix, items, suffix);
549 }
551 Signature_attribute getSignature(Attributes attributes) {
552 if (options.compat) // javap does not recognize recent attributes
553 return null;
554 return (Signature_attribute) attributes.get(Attribute.Signature);
555 }
557 String adjustVarargs(AccessFlags flags, String params) {
558 if (flags.is(ACC_VARARGS) && !options.compat) {
559 int i = params.lastIndexOf("[]");
560 if (i > 0)
561 return params.substring(0, i) + "..." + params.substring(i+2);
562 }
564 return params;
565 }
567 String getJavaName(ClassFile cf) {
568 try {
569 return getJavaName(cf.getName());
570 } catch (ConstantPoolException e) {
571 return report(e);
572 }
573 }
575 String getJavaSuperclassName(ClassFile cf) {
576 try {
577 return getJavaName(cf.getSuperclassName());
578 } catch (ConstantPoolException e) {
579 return report(e);
580 }
581 }
583 String getJavaInterfaceName(ClassFile cf, int index) {
584 try {
585 return getJavaName(cf.getInterfaceName(index));
586 } catch (ConstantPoolException e) {
587 return report(e);
588 }
589 }
591 String getJavaFieldType(Descriptor d) {
592 try {
593 return getJavaName(d.getFieldType(constant_pool));
594 } catch (ConstantPoolException e) {
595 return report(e);
596 } catch (DescriptorException e) {
597 return report(e);
598 }
599 }
601 String getJavaReturnType(Descriptor d) {
602 try {
603 return getJavaName(d.getReturnType(constant_pool));
604 } catch (ConstantPoolException e) {
605 return report(e);
606 } catch (DescriptorException e) {
607 return report(e);
608 }
609 }
611 String getJavaParameterTypes(Descriptor d, AccessFlags flags) {
612 try {
613 return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool)));
614 } catch (ConstantPoolException e) {
615 return report(e);
616 } catch (DescriptorException e) {
617 return report(e);
618 }
619 }
621 String getJavaException(Exceptions_attribute attr, int index) {
622 try {
623 return getJavaName(attr.getException(index, constant_pool));
624 } catch (ConstantPoolException e) {
625 return report(e);
626 }
627 }
629 String getValue(Descriptor d) {
630 try {
631 return d.getValue(constant_pool);
632 } catch (ConstantPoolException e) {
633 return report(e);
634 }
635 }
637 String getFieldName(Field f) {
638 try {
639 return f.getName(constant_pool);
640 } catch (ConstantPoolException e) {
641 return report(e);
642 }
643 }
645 String getName(Method m) {
646 try {
647 return m.getName(constant_pool);
648 } catch (ConstantPoolException e) {
649 return report(e);
650 }
651 }
653 static String getJavaName(String name) {
654 return name.replace('/', '.');
655 }
657 String getSourceFile(SourceFile_attribute attr) {
658 try {
659 return attr.getSourceFile(constant_pool);
660 } catch (ConstantPoolException e) {
661 return report(e);
662 }
663 }
665 /**
666 * Get the value of an entry in the constant pool as a Java constant.
667 * Characters and booleans are represented by CONSTANT_Intgere entries.
668 * Character and string values are processed to escape characters outside
669 * the basic printable ASCII set.
670 * @param d the descriptor, giving the expected type of the constant
671 * @param index the index of the value in the constant pool
672 * @return a printable string containing the value of the constant.
673 */
674 String getConstantValue(Descriptor d, int index) {
675 try {
676 ConstantPool.CPInfo cpInfo = constant_pool.get(index);
678 switch (cpInfo.getTag()) {
679 case ConstantPool.CONSTANT_Integer: {
680 ConstantPool.CONSTANT_Integer_info info =
681 (ConstantPool.CONSTANT_Integer_info) cpInfo;
682 String t = d.getValue(constant_pool);
683 if (t.equals("C")) { // character
684 return getConstantCharValue((char) info.value);
685 } else if (t.equals("Z")) { // boolean
686 return String.valueOf(info.value == 1);
687 } else { // other: assume integer
688 return String.valueOf(info.value);
689 }
690 }
692 case ConstantPool.CONSTANT_String: {
693 ConstantPool.CONSTANT_String_info info =
694 (ConstantPool.CONSTANT_String_info) cpInfo;
695 return getConstantStringValue(info.getString());
696 }
698 default:
699 return constantWriter.stringValue(cpInfo);
700 }
701 } catch (ConstantPoolException e) {
702 return "#" + index;
703 }
704 }
706 private String getConstantCharValue(char c) {
707 StringBuilder sb = new StringBuilder();
708 sb.append('\'');
709 sb.append(esc(c, '\''));
710 sb.append('\'');
711 return sb.toString();
712 }
714 private String getConstantStringValue(String s) {
715 StringBuilder sb = new StringBuilder();
716 sb.append("\"");
717 for (int i = 0; i < s.length(); i++) {
718 sb.append(esc(s.charAt(i), '"'));
719 }
720 sb.append("\"");
721 return sb.toString();
722 }
724 private String esc(char c, char quote) {
725 if (32 <= c && c <= 126 && c != quote)
726 return String.valueOf(c);
727 else switch (c) {
728 case '\b': return "\\b";
729 case '\n': return "\\n";
730 case '\t': return "\\t";
731 case '\f': return "\\f";
732 case '\r': return "\\r";
733 case '\\': return "\\\\";
734 case '\'': return "\\'";
735 case '\"': return "\\\"";
736 default: return String.format("\\u%04x", (int) c);
737 }
738 }
740 private Options options;
741 private AttributeWriter attrWriter;
742 private CodeWriter codeWriter;
743 private ConstantWriter constantWriter;
744 private ClassFile classFile;
745 private URI uri;
746 private long lastModified;
747 private String digestName;
748 private byte[] digest;
749 private int size;
750 private ConstantPool constant_pool;
751 private Method method;
752 private static final String NEWLINE = System.getProperty("line.separator", "\n");
753 }