jjg@46: /* xdono@54: * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. jjg@46: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@46: * jjg@46: * This code is free software; you can redistribute it and/or modify it jjg@46: * under the terms of the GNU General Public License version 2 only, as jjg@46: * published by the Free Software Foundation. Sun designates this jjg@46: * particular file as subject to the "Classpath" exception as provided jjg@46: * by Sun in the LICENSE file that accompanied this code. jjg@46: * jjg@46: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@46: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@46: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@46: * version 2 for more details (a copy is included in the LICENSE file that jjg@46: * accompanied this code). jjg@46: * jjg@46: * You should have received a copy of the GNU General Public License version jjg@46: * 2 along with this work; if not, write to the Free Software Foundation, jjg@46: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@46: * jjg@46: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, jjg@46: * CA 95054 USA or visit www.sun.com if you need additional information or jjg@46: * have any questions. jjg@46: */ jjg@46: jjg@46: package com.sun.tools.javap; jjg@46: jjg@88: import java.net.URI; jjg@46: import java.util.Collection; jjg@46: import java.util.List; jjg@46: jjg@46: import com.sun.tools.classfile.AccessFlags; jjg@46: import com.sun.tools.classfile.Attribute; jjg@46: import com.sun.tools.classfile.Attributes; jjg@46: import com.sun.tools.classfile.ClassFile; jjg@46: import com.sun.tools.classfile.Code_attribute; jjg@46: import com.sun.tools.classfile.ConstantPool; jjg@46: import com.sun.tools.classfile.ConstantPoolException; jjg@87: import com.sun.tools.classfile.ConstantValue_attribute; jjg@46: import com.sun.tools.classfile.Descriptor; jjg@46: import com.sun.tools.classfile.DescriptorException; jjg@46: import com.sun.tools.classfile.Exceptions_attribute; jjg@46: import com.sun.tools.classfile.Field; jjg@46: import com.sun.tools.classfile.Method; jjg@46: import com.sun.tools.classfile.Signature; jjg@46: import com.sun.tools.classfile.Signature_attribute; jjg@46: import com.sun.tools.classfile.SourceFile_attribute; jjg@46: import com.sun.tools.classfile.Type; jjg@46: jjg@88: import java.text.DateFormat; jjg@88: import java.util.Date; jjg@46: import static com.sun.tools.classfile.AccessFlags.*; jjg@46: jjg@46: /* jjg@46: * The main javap class to write the contents of a class file as text. jjg@46: * jjg@46: *

This is NOT part of any API supported by Sun Microsystems. If jjg@46: * you write code that depends on this, you do so at your own risk. jjg@46: * This code and its internal interfaces are subject to change or jjg@46: * deletion without notice. jjg@46: */ jjg@46: public class ClassWriter extends BasicWriter { jjg@46: static ClassWriter instance(Context context) { jjg@46: ClassWriter instance = context.get(ClassWriter.class); jjg@46: if (instance == null) jjg@46: instance = new ClassWriter(context); jjg@46: return instance; jjg@46: } jjg@46: jjg@46: protected ClassWriter(Context context) { jjg@46: super(context); jjg@46: context.put(ClassWriter.class, this); jjg@46: options = Options.instance(context); jjg@46: attrWriter = AttributeWriter.instance(context); jjg@46: codeWriter = CodeWriter.instance(context); jjg@46: constantWriter = ConstantWriter.instance(context); jjg@46: } jjg@46: jjg@88: void setDigest(String name, byte[] digest) { jjg@88: this.digestName = name; jjg@88: this.digest = digest; jjg@88: } jjg@88: jjg@88: void setFile(URI uri) { jjg@88: this.uri = uri; jjg@88: } jjg@88: jjg@88: void setFileSize(int size) { jjg@88: this.size = size; jjg@88: } jjg@88: jjg@88: void setLastModified(long lastModified) { jjg@88: this.lastModified = lastModified; jjg@88: } jjg@88: jjg@46: ClassFile getClassFile() { jjg@46: return classFile; jjg@46: } jjg@46: jjg@46: Method getMethod() { jjg@46: return method; jjg@46: } jjg@46: jjg@46: public void write(ClassFile cf) { jjg@46: classFile = cf; jjg@46: constant_pool = classFile.constant_pool; jjg@46: jjg@88: if ((options.sysInfo || options.verbose) && !options.compat) { jjg@88: if (uri != null) { jjg@88: if (uri.getScheme().equals("file")) jjg@88: println("Classfile " + uri.getPath()); jjg@88: else jjg@88: println("Classfile " + uri); jjg@88: } jjg@88: if (lastModified != -1) { jjg@88: Date lm = new Date(lastModified); jjg@88: DateFormat df = DateFormat.getDateInstance(); jjg@88: if (size > 0) { jjg@88: println("Last modified " + df.format(lm) + "; size " + size + " bytes"); jjg@88: } else { jjg@88: println("Last modified " + df.format(lm)); jjg@88: } jjg@88: } else if (size > 0) { jjg@88: println("Size " + size + " bytes"); jjg@88: } jjg@88: if (digestName != null && digest != null) { jjg@88: StringBuilder sb = new StringBuilder(); jjg@88: for (byte b: digest) jjg@88: sb.append(String.format("%02x", b)); jjg@88: println(digestName + " checksum " + sb); jjg@88: } jjg@88: } jjg@88: jjg@46: Attribute sfa = cf.getAttribute(Attribute.SourceFile); jjg@46: if (sfa instanceof SourceFile_attribute) { jjg@46: println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\""); jjg@46: } jjg@46: jjg@46: String name = getJavaName(classFile); jjg@46: AccessFlags flags = cf.access_flags; jjg@46: jjg@46: writeModifiers(flags.getClassModifiers()); jjg@46: jjg@46: if (classFile.isClass()) jjg@46: print("class "); jjg@46: else if (classFile.isInterface()) jjg@46: print("interface "); jjg@46: jjg@46: print(name); jjg@46: jjg@46: Signature_attribute sigAttr = getSignature(cf.attributes); jjg@46: if (sigAttr == null) { jjg@46: // use info from class file header jjg@65: if (classFile.isClass() && classFile.super_class != 0 ) { jjg@65: String sn = getJavaSuperclassName(cf); jjg@65: print(" extends "); jjg@65: print(sn); jjg@46: } jjg@46: for (int i = 0; i < classFile.interfaces.length; i++) { jjg@46: print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ","); jjg@46: print(getJavaInterfaceName(classFile, i)); jjg@46: } jjg@46: } else { jjg@46: try { jjg@46: Type t = sigAttr.getParsedSignature().getType(constant_pool); jjg@46: // The signature parser cannot disambiguate between a jjg@46: // FieldType and a ClassSignatureType that only contains a superclass type. jjg@46: if (t instanceof Type.ClassSigType) jjg@46: print(t); jjg@65: else { jjg@46: print(" extends "); jjg@46: print(t); jjg@46: } jjg@46: } catch (ConstantPoolException e) { jjg@46: print(report(e)); jjg@46: } jjg@46: } jjg@46: jjg@46: if (options.verbose) { jjg@46: println(); jjg@46: attrWriter.write(cf, cf.attributes, constant_pool); jjg@46: println(" minor version: " + cf.minor_version); jjg@46: println(" major version: " + cf.major_version); jjg@46: if (!options.compat) jjg@46: writeList(" flags: ", flags.getClassFlags(), NEWLINE); jjg@46: constantWriter.writeConstantPool(); jjg@46: println(); jjg@46: } else { jjg@46: if (!options.compat) jjg@46: print(" "); jjg@46: } jjg@46: jjg@46: println("{"); jjg@46: writeFields(); jjg@46: writeMethods(); jjg@46: println("}"); jjg@46: println(); jjg@46: } jjg@46: jjg@46: void writeFields() { jjg@46: for (Field f: classFile.fields) { jjg@46: writeField(f); jjg@46: } jjg@46: } jjg@46: jjg@46: void writeField(Field f) { jjg@46: if (!options.checkAccess(f.access_flags)) jjg@46: return; jjg@46: jjg@46: if (!(options.showLineAndLocalVariableTables jjg@46: || options.showDisassembled jjg@46: || options.verbose jjg@46: || options.showInternalSignatures jjg@46: || options.showAllAttrs)) { jjg@46: print(" "); jjg@46: } jjg@46: jjg@46: AccessFlags flags = f.access_flags; jjg@46: writeModifiers(flags.getFieldModifiers()); jjg@46: Signature_attribute sigAttr = getSignature(f.attributes); jjg@46: if (sigAttr == null) jjg@46: print(getFieldType(f.descriptor)); jjg@46: else { jjg@46: try { jjg@46: Type t = sigAttr.getParsedSignature().getType(constant_pool); jjg@46: print(t); jjg@46: } catch (ConstantPoolException e) { jjg@46: // report error? jjg@46: // fall back on non-generic descriptor jjg@46: print(getFieldType(f.descriptor)); jjg@46: } jjg@46: } jjg@46: print(" "); jjg@46: print(getFieldName(f)); jjg@87: if (options.showConstants && !options.compat) { // BUG 4111861 print static final field contents jjg@87: Attribute a = f.attributes.get(Attribute.ConstantValue); jjg@87: if (a instanceof ConstantValue_attribute) { jjg@87: print(" = "); jjg@87: ConstantValue_attribute cv = (ConstantValue_attribute) a; jjg@87: print(getConstantValue(f.descriptor, cv.constantvalue_index)); jjg@87: } jjg@87: } jjg@46: print(";"); jjg@46: println(); jjg@46: jjg@46: if (options.showInternalSignatures) jjg@46: println(" Signature: " + getValue(f.descriptor)); jjg@46: jjg@46: if (options.verbose && !options.compat) jjg@46: writeList(" flags: ", flags.getFieldFlags(), NEWLINE); jjg@46: jjg@46: if (options.showAllAttrs) { jjg@46: for (Attribute attr: f.attributes) jjg@46: attrWriter.write(f, attr, constant_pool); jjg@46: println(); jjg@46: } jjg@46: jjg@46: if (options.showDisassembled || options.showLineAndLocalVariableTables) jjg@46: println(); jjg@46: } jjg@46: jjg@46: void writeMethods() { jjg@46: for (Method m: classFile.methods) jjg@46: writeMethod(m); jjg@46: } jjg@46: jjg@46: void writeMethod(Method m) { jjg@46: if (!options.checkAccess(m.access_flags)) jjg@46: return; jjg@46: jjg@46: method = m; jjg@46: jjg@46: if (!(options.showLineAndLocalVariableTables jjg@46: || options.showDisassembled jjg@46: || options.verbose jjg@46: || options.showInternalSignatures jjg@46: || options.showAllAttrs)) { jjg@46: print(" "); jjg@46: } jjg@46: jjg@46: AccessFlags flags = m.access_flags; jjg@46: jjg@46: Descriptor d; jjg@46: Type.MethodType methodType; jjg@46: List methodExceptions; jjg@46: jjg@46: Signature_attribute sigAttr = getSignature(m.attributes); jjg@46: if (sigAttr == null) { jjg@46: d = m.descriptor; jjg@46: methodType = null; jjg@46: methodExceptions = null; jjg@46: } else { jjg@46: Signature methodSig = sigAttr.getParsedSignature(); jjg@46: d = methodSig; jjg@46: try { jjg@46: methodType = (Type.MethodType) methodSig.getType(constant_pool); jjg@46: methodExceptions = methodType.throwsTypes; jjg@46: if (methodExceptions != null && methodExceptions.size() == 0) jjg@46: methodExceptions = null; jjg@46: } catch (ConstantPoolException e) { jjg@46: // report error? jjg@46: // fall back on standard descriptor jjg@46: methodType = null; jjg@46: methodExceptions = null; jjg@46: } jjg@46: } jjg@46: jjg@46: writeModifiers(flags.getMethodModifiers()); jjg@46: if (methodType != null) { jjg@46: writeListIfNotEmpty("<", methodType.typeArgTypes, "> "); jjg@46: } jjg@46: if (getName(m).equals("")) { jjg@46: print(getJavaName(classFile)); jjg@46: print(getParameterTypes(d, flags)); jjg@46: } else if (getName(m).equals("")) { jjg@46: print("{}"); jjg@46: } else { jjg@46: print(getReturnType(d)); jjg@46: print(" "); jjg@46: print(getName(m)); jjg@46: print(getParameterTypes(d, flags)); jjg@46: } jjg@46: jjg@46: Attribute e_attr = m.attributes.get(Attribute.Exceptions); jjg@46: if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions jjg@46: if (e_attr instanceof Exceptions_attribute) { jjg@46: Exceptions_attribute exceptions = (Exceptions_attribute) e_attr; jjg@46: if (options.compat) { // Bug XXXXXXX whitespace jjg@46: if (!(options.showLineAndLocalVariableTables jjg@46: || options.showDisassembled jjg@46: || options.verbose jjg@46: || options.showInternalSignatures jjg@46: || options.showAllAttrs)) { jjg@46: print(" "); jjg@46: } jjg@46: print(" "); jjg@46: } jjg@46: print(" throws "); jjg@46: if (methodExceptions != null) { // use generic list if available jjg@46: writeList("", methodExceptions, ""); jjg@46: } else { jjg@46: for (int i = 0; i < exceptions.number_of_exceptions; i++) { jjg@46: if (i > 0) jjg@46: print(", "); jjg@52: print(getJavaException(exceptions, i)); jjg@46: } jjg@46: } jjg@46: } else { jjg@46: report("Unexpected or invalid value for Exceptions attribute"); jjg@46: } jjg@46: } jjg@46: jjg@46: print(";"); jjg@46: println(); jjg@46: jjg@46: if (options.showInternalSignatures) jjg@46: println(" Signature: " + getValue(m.descriptor)); jjg@46: jjg@46: if (options.verbose && !options.compat) jjg@46: writeList(" flags: ", flags.getMethodFlags(), NEWLINE); jjg@46: jjg@46: Code_attribute code = null; jjg@46: Attribute c_attr = m.attributes.get(Attribute.Code); jjg@46: if (c_attr != null) { jjg@46: if (c_attr instanceof Code_attribute) jjg@46: code = (Code_attribute) c_attr; jjg@46: else jjg@46: report("Unexpected or invalid value for Code attribute"); jjg@46: } jjg@46: jjg@46: if (options.showDisassembled && !options.showAllAttrs) { jjg@46: if (code != null) { jjg@46: println(" Code:"); jjg@46: codeWriter.writeInstrs(code); jjg@46: codeWriter.writeExceptionTable(code); jjg@46: } jjg@46: println(); jjg@46: } jjg@46: jjg@46: if (options.showLineAndLocalVariableTables) { jjg@46: if (code != null) jjg@46: attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool); jjg@46: println(); jjg@46: if (code != null) jjg@46: attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool); jjg@46: println(); jjg@46: println(); jjg@46: } jjg@46: jjg@46: if (options.showAllAttrs) { jjg@46: Attribute[] attrs = m.attributes.attrs; jjg@46: for (Attribute attr: attrs) jjg@46: attrWriter.write(m, attr, constant_pool); jjg@46: jjg@46: // // the following condition is to mimic old javap jjg@46: // if (!(attrs.length > 0 && jjg@46: // attrs[attrs.length - 1] instanceof Exceptions_attribute)) jjg@46: println(); jjg@46: } jjg@46: } jjg@46: jjg@46: void writeModifiers(Collection items) { jjg@46: for (Object item: items) { jjg@46: print(item); jjg@46: print(" "); jjg@46: } jjg@46: } jjg@46: jjg@46: void writeList(String prefix, Collection items, String suffix) { jjg@46: print(prefix); jjg@46: String sep = ""; jjg@46: for (Object item: items) { jjg@46: print(sep); jjg@46: print(item); jjg@46: sep = ", "; jjg@46: } jjg@46: print(suffix); jjg@46: } jjg@46: jjg@46: void writeListIfNotEmpty(String prefix, List items, String suffix) { jjg@46: if (items != null && items.size() > 0) jjg@46: writeList(prefix, items, suffix); jjg@46: } jjg@46: jjg@46: Signature_attribute getSignature(Attributes attributes) { jjg@46: if (options.compat) // javap does not recognize recent attributes jjg@46: return null; jjg@46: return (Signature_attribute) attributes.get(Attribute.Signature); jjg@46: } jjg@46: jjg@46: String adjustVarargs(AccessFlags flags, String params) { jjg@46: if (flags.is(ACC_VARARGS) && !options.compat) { jjg@46: int i = params.lastIndexOf("[]"); jjg@46: if (i > 0) jjg@46: return params.substring(0, i) + "..." + params.substring(i+2); jjg@46: } jjg@46: jjg@46: return params; jjg@46: } jjg@46: jjg@46: String getJavaName(ClassFile cf) { jjg@46: try { jjg@46: return getJavaName(cf.getName()); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: String getJavaSuperclassName(ClassFile cf) { jjg@46: try { jjg@46: return getJavaName(cf.getSuperclassName()); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: String getJavaInterfaceName(ClassFile cf, int index) { jjg@46: try { jjg@46: return getJavaName(cf.getInterfaceName(index)); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: String getFieldType(Descriptor d) { jjg@46: try { jjg@46: return d.getFieldType(constant_pool); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } catch (DescriptorException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: String getReturnType(Descriptor d) { jjg@46: try { jjg@46: return d.getReturnType(constant_pool); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } catch (DescriptorException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: String getParameterTypes(Descriptor d, AccessFlags flags) { jjg@46: try { jjg@46: return adjustVarargs(flags, d.getParameterTypes(constant_pool)); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } catch (DescriptorException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@52: String getJavaException(Exceptions_attribute attr, int index) { jjg@52: try { jjg@52: return getJavaName(attr.getException(index, constant_pool)); jjg@52: } catch (ConstantPoolException e) { jjg@52: return report(e); jjg@52: } jjg@52: } jjg@52: jjg@46: String getValue(Descriptor d) { jjg@46: try { jjg@46: return d.getValue(constant_pool); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: String getFieldName(Field f) { jjg@46: try { jjg@46: return f.getName(constant_pool); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: String getName(Method m) { jjg@46: try { jjg@46: return m.getName(constant_pool); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@46: static String getJavaName(String name) { jjg@46: return name.replace('/', '.'); jjg@46: } jjg@46: jjg@46: String getSourceFile(SourceFile_attribute attr) { jjg@46: try { jjg@46: return attr.getSourceFile(constant_pool); jjg@46: } catch (ConstantPoolException e) { jjg@46: return report(e); jjg@46: } jjg@46: } jjg@46: jjg@87: /** jjg@87: * Get the value of an entry in the constant pool as a Java constant. jjg@87: * Characters and booleans are represented by CONSTANT_Intgere entries. jjg@87: * Character and string values are processed to escape characters outside jjg@87: * the basic printable ASCII set. jjg@87: * @param d the descriptor, giving the expected type of the constant jjg@87: * @param index the index of the value in the constant pool jjg@87: * @return a printable string containing the value of the constant. jjg@87: */ jjg@87: String getConstantValue(Descriptor d, int index) { jjg@87: try { jjg@87: ConstantPool.CPInfo cpInfo = constant_pool.get(index); jjg@87: jjg@87: switch (cpInfo.getTag()) { jjg@87: case ConstantPool.CONSTANT_Integer: { jjg@87: ConstantPool.CONSTANT_Integer_info info = jjg@87: (ConstantPool.CONSTANT_Integer_info) cpInfo; jjg@87: String t = d.getValue(constant_pool); jjg@87: if (t.equals("C")) { // character jjg@87: return getConstantCharValue((char) info.value); jjg@87: } else if (t.equals("Z")) { // boolean jjg@87: return String.valueOf(info.value == 1); jjg@87: } else { // other: assume integer jjg@87: return String.valueOf(info.value); jjg@87: } jjg@87: } jjg@87: jjg@87: case ConstantPool.CONSTANT_String: { jjg@87: ConstantPool.CONSTANT_String_info info = jjg@87: (ConstantPool.CONSTANT_String_info) cpInfo; jjg@87: return getConstantStringValue(info.getString()); jjg@87: } jjg@87: jjg@87: default: jjg@87: return constantWriter.stringValue(cpInfo); jjg@87: } jjg@87: } catch (ConstantPoolException e) { jjg@87: return "#" + index; jjg@87: } jjg@87: } jjg@87: jjg@87: private String getConstantCharValue(char c) { jjg@87: StringBuilder sb = new StringBuilder(); jjg@87: sb.append('\''); jjg@87: sb.append(esc(c, '\'')); jjg@87: sb.append('\''); jjg@87: return sb.toString(); jjg@87: } jjg@87: jjg@87: private String getConstantStringValue(String s) { jjg@87: StringBuilder sb = new StringBuilder(); jjg@87: sb.append("\""); jjg@87: for (int i = 0; i < s.length(); i++) { jjg@87: sb.append(esc(s.charAt(i), '"')); jjg@87: } jjg@87: sb.append("\""); jjg@87: return sb.toString(); jjg@87: } jjg@87: jjg@87: private String esc(char c, char quote) { jjg@87: if (32 <= c && c <= 126 && c != quote) jjg@87: return String.valueOf(c); jjg@87: else switch (c) { jjg@87: case '\b': return "\\b"; jjg@87: case '\n': return "\\n"; jjg@87: case '\t': return "\\t"; jjg@87: case '\f': return "\\f"; jjg@87: case '\r': return "\\r"; jjg@87: case '\\': return "\\\\"; jjg@87: case '\'': return "\\'"; jjg@87: case '\"': return "\\\""; jjg@87: default: return String.format("\\u%04x", (int) c); jjg@87: } jjg@87: } jjg@87: jjg@46: private Options options; jjg@46: private AttributeWriter attrWriter; jjg@46: private CodeWriter codeWriter; jjg@46: private ConstantWriter constantWriter; jjg@46: private ClassFile classFile; jjg@88: private URI uri; jjg@88: private long lastModified; jjg@88: private String digestName; jjg@88: private byte[] digest; jjg@88: private int size; jjg@46: private ConstantPool constant_pool; jjg@46: private Method method; jjg@46: private static final String NEWLINE = System.getProperty("line.separator", "\n"); jjg@46: }