langtools/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java
changeset 30846 2b3f379840f0
parent 25874 83c19f00452c
child 36526 3b41f1c69604
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java	Wed May 27 13:25:18 2015 -0700
@@ -0,0 +1,756 @@
+/*
+ * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javap;
+
+import java.net.URI;
+import java.text.DateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import com.sun.tools.classfile.AccessFlags;
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.Attributes;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.ConstantPool;
+import com.sun.tools.classfile.ConstantPoolException;
+import com.sun.tools.classfile.ConstantValue_attribute;
+import com.sun.tools.classfile.Descriptor;
+import com.sun.tools.classfile.DescriptorException;
+import com.sun.tools.classfile.Exceptions_attribute;
+import com.sun.tools.classfile.Field;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.classfile.Signature;
+import com.sun.tools.classfile.Signature_attribute;
+import com.sun.tools.classfile.SourceFile_attribute;
+import com.sun.tools.classfile.Type;
+import com.sun.tools.classfile.Type.ArrayType;
+import com.sun.tools.classfile.Type.ClassSigType;
+import com.sun.tools.classfile.Type.ClassType;
+import com.sun.tools.classfile.Type.MethodType;
+import com.sun.tools.classfile.Type.SimpleType;
+import com.sun.tools.classfile.Type.TypeParamType;
+import com.sun.tools.classfile.Type.WildcardType;
+
+import static com.sun.tools.classfile.AccessFlags.*;
+
+/*
+ *  The main javap class to write the contents of a class file as text.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class ClassWriter extends BasicWriter {
+    static ClassWriter instance(Context context) {
+        ClassWriter instance = context.get(ClassWriter.class);
+        if (instance == null)
+            instance = new ClassWriter(context);
+        return instance;
+    }
+
+    protected ClassWriter(Context context) {
+        super(context);
+        context.put(ClassWriter.class, this);
+        options = Options.instance(context);
+        attrWriter = AttributeWriter.instance(context);
+        codeWriter = CodeWriter.instance(context);
+        constantWriter = ConstantWriter.instance(context);
+    }
+
+    void setDigest(String name, byte[] digest) {
+        this.digestName = name;
+        this.digest = digest;
+    }
+
+    void setFile(URI uri) {
+        this.uri = uri;
+    }
+
+    void setFileSize(int size) {
+        this.size = size;
+    }
+
+    void setLastModified(long lastModified) {
+        this.lastModified = lastModified;
+    }
+
+    protected ClassFile getClassFile() {
+        return classFile;
+    }
+
+    protected void setClassFile(ClassFile cf) {
+        classFile = cf;
+        constant_pool = classFile.constant_pool;
+    }
+
+    protected Method getMethod() {
+        return method;
+    }
+
+    protected void setMethod(Method m) {
+        method = m;
+    }
+
+    public void write(ClassFile cf) {
+        setClassFile(cf);
+
+        if (options.sysInfo || options.verbose) {
+            if (uri != null) {
+                if (uri.getScheme().equals("file"))
+                    println("Classfile " + uri.getPath());
+                else
+                    println("Classfile " + uri);
+            }
+            indent(+1);
+            if (lastModified != -1) {
+                Date lm = new Date(lastModified);
+                DateFormat df = DateFormat.getDateInstance();
+                if (size > 0) {
+                    println("Last modified " + df.format(lm) + "; size " + size + " bytes");
+                } else {
+                    println("Last modified " + df.format(lm));
+                }
+            } else if (size > 0) {
+                println("Size " + size + " bytes");
+            }
+            if (digestName != null && digest != null) {
+                StringBuilder sb = new StringBuilder();
+                for (byte b: digest)
+                    sb.append(String.format("%02x", b));
+                println(digestName + " checksum " + sb);
+            }
+        }
+
+        Attribute sfa = cf.getAttribute(Attribute.SourceFile);
+        if (sfa instanceof SourceFile_attribute) {
+            println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\"");
+        }
+
+        if (options.sysInfo || options.verbose) {
+            indent(-1);
+        }
+
+        String name = getJavaName(classFile);
+        AccessFlags flags = cf.access_flags;
+
+        writeModifiers(flags.getClassModifiers());
+
+        if (classFile.isClass())
+            print("class ");
+        else if (classFile.isInterface())
+            print("interface ");
+
+        print(name);
+
+        Signature_attribute sigAttr = getSignature(cf.attributes);
+        if (sigAttr == null) {
+            // use info from class file header
+            if (classFile.isClass() && classFile.super_class != 0 ) {
+                String sn = getJavaSuperclassName(cf);
+                if (!sn.equals("java.lang.Object")) {
+                    print(" extends ");
+                    print(sn);
+                }
+            }
+            for (int i = 0; i < classFile.interfaces.length; i++) {
+                print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ",");
+                print(getJavaInterfaceName(classFile, i));
+            }
+        } else {
+            try {
+                Type t = sigAttr.getParsedSignature().getType(constant_pool);
+                JavaTypePrinter p = new JavaTypePrinter(classFile.isInterface());
+                // The signature parser cannot disambiguate between a
+                // FieldType and a ClassSignatureType that only contains a superclass type.
+                if (t instanceof Type.ClassSigType) {
+                    print(p.print(t));
+                } else if (options.verbose || !t.isObject()) {
+                    print(" extends ");
+                    print(p.print(t));
+                }
+            } catch (ConstantPoolException e) {
+                print(report(e));
+            }
+        }
+
+        if (options.verbose) {
+            println();
+            indent(+1);
+            println("minor version: " + cf.minor_version);
+            println("major version: " + cf.major_version);
+            writeList("flags: ", flags.getClassFlags(), "\n");
+            indent(-1);
+            constantWriter.writeConstantPool();
+        } else {
+            print(" ");
+        }
+
+        println("{");
+        indent(+1);
+        writeFields();
+        writeMethods();
+        indent(-1);
+        println("}");
+
+        if (options.verbose) {
+            attrWriter.write(cf, cf.attributes, constant_pool);
+        }
+    }
+    // where
+        class JavaTypePrinter implements Type.Visitor<StringBuilder,StringBuilder> {
+            boolean isInterface;
+
+            JavaTypePrinter(boolean isInterface) {
+                this.isInterface = isInterface;
+            }
+
+            String print(Type t) {
+                return t.accept(this, new StringBuilder()).toString();
+            }
+
+            String printTypeArgs(List<? extends TypeParamType> typeParamTypes) {
+                StringBuilder builder = new StringBuilder();
+                appendIfNotEmpty(builder, "<", typeParamTypes, "> ");
+                return builder.toString();
+            }
+
+            public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) {
+                sb.append(getJavaName(type.name));
+                return sb;
+            }
+
+            public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) {
+                append(sb, type.elemType);
+                sb.append("[]");
+                return sb;
+            }
+
+            public StringBuilder visitMethodType(MethodType type, StringBuilder sb) {
+                appendIfNotEmpty(sb, "<", type.typeParamTypes, "> ");
+                append(sb, type.returnType);
+                append(sb, " (", type.paramTypes, ")");
+                appendIfNotEmpty(sb, " throws ", type.throwsTypes, "");
+                return sb;
+            }
+
+            public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) {
+                appendIfNotEmpty(sb, "<", type.typeParamTypes, ">");
+                if (isInterface) {
+                    appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, "");
+                } else {
+                    if (type.superclassType != null
+                            && (options.verbose || !type.superclassType.isObject())) {
+                        sb.append(" extends ");
+                        append(sb, type.superclassType);
+                    }
+                    appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, "");
+                }
+                return sb;
+            }
+
+            public StringBuilder visitClassType(ClassType type, StringBuilder sb) {
+                if (type.outerType != null) {
+                    append(sb, type.outerType);
+                    sb.append(".");
+                }
+                sb.append(getJavaName(type.name));
+                appendIfNotEmpty(sb, "<", type.typeArgs, ">");
+                return sb;
+            }
+
+            public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) {
+                sb.append(type.name);
+                String sep = " extends ";
+                if (type.classBound != null
+                        && (options.verbose || !type.classBound.isObject())) {
+                    sb.append(sep);
+                    append(sb, type.classBound);
+                    sep = " & ";
+                }
+                if (type.interfaceBounds != null) {
+                    for (Type bound: type.interfaceBounds) {
+                        sb.append(sep);
+                        append(sb, bound);
+                        sep = " & ";
+                    }
+                }
+                return sb;
+            }
+
+            public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) {
+                switch (type.kind) {
+                    case UNBOUNDED:
+                        sb.append("?");
+                        break;
+                    case EXTENDS:
+                        sb.append("? extends ");
+                        append(sb, type.boundType);
+                        break;
+                    case SUPER:
+                        sb.append("? super ");
+                        append(sb, type.boundType);
+                        break;
+                    default:
+                        throw new AssertionError();
+                }
+                return sb;
+            }
+
+            private void append(StringBuilder sb, Type t) {
+                t.accept(this, sb);
+            }
+
+            private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
+                sb.append(prefix);
+                String sep = "";
+                for (Type t: list) {
+                    sb.append(sep);
+                    append(sb, t);
+                    sep = ", ";
+                }
+                sb.append(suffix);
+            }
+
+            private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
+                if (!isEmpty(list))
+                    append(sb, prefix, list, suffix);
+            }
+
+            private boolean isEmpty(List<? extends Type> list) {
+                return (list == null || list.isEmpty());
+            }
+        }
+
+    protected void writeFields() {
+        for (Field f: classFile.fields) {
+            writeField(f);
+        }
+    }
+
+    protected void writeField(Field f) {
+        if (!options.checkAccess(f.access_flags))
+            return;
+
+        AccessFlags flags = f.access_flags;
+        writeModifiers(flags.getFieldModifiers());
+        Signature_attribute sigAttr = getSignature(f.attributes);
+        if (sigAttr == null)
+            print(getJavaFieldType(f.descriptor));
+        else {
+            try {
+                Type t = sigAttr.getParsedSignature().getType(constant_pool);
+                print(getJavaName(t.toString()));
+            } catch (ConstantPoolException e) {
+                // report error?
+                // fall back on non-generic descriptor
+                print(getJavaFieldType(f.descriptor));
+            }
+        }
+        print(" ");
+        print(getFieldName(f));
+        if (options.showConstants) {
+            Attribute a = f.attributes.get(Attribute.ConstantValue);
+            if (a instanceof ConstantValue_attribute) {
+                print(" = ");
+                ConstantValue_attribute cv = (ConstantValue_attribute) a;
+                print(getConstantValue(f.descriptor, cv.constantvalue_index));
+            }
+        }
+        print(";");
+        println();
+
+        indent(+1);
+
+        boolean showBlank = false;
+
+        if (options.showDescriptors)
+            println("descriptor: " + getValue(f.descriptor));
+
+        if (options.verbose)
+            writeList("flags: ", flags.getFieldFlags(), "\n");
+
+        if (options.showAllAttrs) {
+            for (Attribute attr: f.attributes)
+                attrWriter.write(f, attr, constant_pool);
+            showBlank = true;
+        }
+
+        indent(-1);
+
+        if (showBlank || options.showDisassembled || options.showLineAndLocalVariableTables)
+            println();
+    }
+
+    protected void writeMethods() {
+        for (Method m: classFile.methods)
+            writeMethod(m);
+        setPendingNewline(false);
+    }
+
+    protected void writeMethod(Method m) {
+        if (!options.checkAccess(m.access_flags))
+            return;
+
+        method = m;
+
+        AccessFlags flags = m.access_flags;
+
+        Descriptor d;
+        Type.MethodType methodType;
+        List<? extends Type> methodExceptions;
+
+        Signature_attribute sigAttr = getSignature(m.attributes);
+        if (sigAttr == null) {
+            d = m.descriptor;
+            methodType = null;
+            methodExceptions = null;
+        } else {
+            Signature methodSig = sigAttr.getParsedSignature();
+            d = methodSig;
+            try {
+                methodType = (Type.MethodType) methodSig.getType(constant_pool);
+                methodExceptions = methodType.throwsTypes;
+                if (methodExceptions != null && methodExceptions.isEmpty())
+                    methodExceptions = null;
+            } catch (ConstantPoolException e) {
+                // report error?
+                // fall back on standard descriptor
+                methodType = null;
+                methodExceptions = null;
+            }
+        }
+
+        writeModifiers(flags.getMethodModifiers());
+        if (methodType != null) {
+            print(new JavaTypePrinter(false).printTypeArgs(methodType.typeParamTypes));
+        }
+        if (getName(m).equals("<init>")) {
+            print(getJavaName(classFile));
+            print(getJavaParameterTypes(d, flags));
+        } else if (getName(m).equals("<clinit>")) {
+            print("{}");
+        } else {
+            print(getJavaReturnType(d));
+            print(" ");
+            print(getName(m));
+            print(getJavaParameterTypes(d, flags));
+        }
+
+        Attribute e_attr = m.attributes.get(Attribute.Exceptions);
+        if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
+            if (e_attr instanceof Exceptions_attribute) {
+                Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
+                print(" throws ");
+                if (methodExceptions != null) { // use generic list if available
+                    writeList("", methodExceptions, "");
+                } else {
+                    for (int i = 0; i < exceptions.number_of_exceptions; i++) {
+                        if (i > 0)
+                            print(", ");
+                        print(getJavaException(exceptions, i));
+                    }
+                }
+            } else {
+                report("Unexpected or invalid value for Exceptions attribute");
+            }
+        }
+
+        println(";");
+
+        indent(+1);
+
+        if (options.showDescriptors) {
+            println("descriptor: " + getValue(m.descriptor));
+        }
+
+        if (options.verbose) {
+            writeList("flags: ", flags.getMethodFlags(), "\n");
+        }
+
+        Code_attribute code = null;
+        Attribute c_attr = m.attributes.get(Attribute.Code);
+        if (c_attr != null) {
+            if (c_attr instanceof Code_attribute)
+                code = (Code_attribute) c_attr;
+            else
+                report("Unexpected or invalid value for Code attribute");
+        }
+
+        if (options.showAllAttrs) {
+            Attribute[] attrs = m.attributes.attrs;
+            for (Attribute attr: attrs)
+                attrWriter.write(m, attr, constant_pool);
+        } else if (code != null) {
+            if (options.showDisassembled) {
+                println("Code:");
+                codeWriter.writeInstrs(code);
+                codeWriter.writeExceptionTable(code);
+            }
+
+            if (options.showLineAndLocalVariableTables) {
+                attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
+                attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
+            }
+        }
+
+        indent(-1);
+
+        // set pendingNewline to write a newline before the next method (if any)
+        // if a separator is desired
+        setPendingNewline(
+                options.showDisassembled ||
+                options.showAllAttrs ||
+                options.showDescriptors ||
+                options.showLineAndLocalVariableTables ||
+                options.verbose);
+    }
+
+    void writeModifiers(Collection<String> items) {
+        for (Object item: items) {
+            print(item);
+            print(" ");
+        }
+    }
+
+    void writeList(String prefix, Collection<?> items, String suffix) {
+        print(prefix);
+        String sep = "";
+        for (Object item: items) {
+            print(sep);
+            print(item);
+            sep = ", ";
+        }
+        print(suffix);
+    }
+
+    void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
+        if (items != null && items.size() > 0)
+            writeList(prefix, items, suffix);
+    }
+
+    Signature_attribute getSignature(Attributes attributes) {
+        return (Signature_attribute) attributes.get(Attribute.Signature);
+    }
+
+    String adjustVarargs(AccessFlags flags, String params) {
+        if (flags.is(ACC_VARARGS)) {
+            int i = params.lastIndexOf("[]");
+            if (i > 0)
+                return params.substring(0, i) + "..." + params.substring(i+2);
+        }
+
+        return params;
+    }
+
+    String getJavaName(ClassFile cf) {
+        try {
+            return getJavaName(cf.getName());
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    String getJavaSuperclassName(ClassFile cf) {
+        try {
+            return getJavaName(cf.getSuperclassName());
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    String getJavaInterfaceName(ClassFile cf, int index) {
+        try {
+            return getJavaName(cf.getInterfaceName(index));
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    String getJavaFieldType(Descriptor d) {
+        try {
+            return getJavaName(d.getFieldType(constant_pool));
+        } catch (ConstantPoolException e) {
+            return report(e);
+        } catch (DescriptorException e) {
+            return report(e);
+        }
+    }
+
+    String getJavaReturnType(Descriptor d) {
+        try {
+            return getJavaName(d.getReturnType(constant_pool));
+        } catch (ConstantPoolException e) {
+            return report(e);
+        } catch (DescriptorException e) {
+            return report(e);
+        }
+    }
+
+    String getJavaParameterTypes(Descriptor d, AccessFlags flags) {
+        try {
+            return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool)));
+        } catch (ConstantPoolException e) {
+            return report(e);
+        } catch (DescriptorException e) {
+            return report(e);
+        }
+    }
+
+    String getJavaException(Exceptions_attribute attr, int index) {
+        try {
+            return getJavaName(attr.getException(index, constant_pool));
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    String getValue(Descriptor d) {
+        try {
+            return d.getValue(constant_pool);
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    String getFieldName(Field f) {
+        try {
+            return f.getName(constant_pool);
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    String getName(Method m) {
+        try {
+            return m.getName(constant_pool);
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    static String getJavaName(String name) {
+        return name.replace('/', '.');
+    }
+
+    String getSourceFile(SourceFile_attribute attr) {
+        try {
+            return attr.getSourceFile(constant_pool);
+        } catch (ConstantPoolException e) {
+            return report(e);
+        }
+    }
+
+    /**
+     * Get the value of an entry in the constant pool as a Java constant.
+     * Characters and booleans are represented by CONSTANT_Intgere entries.
+     * Character and string values are processed to escape characters outside
+     * the basic printable ASCII set.
+     * @param d the descriptor, giving the expected type of the constant
+     * @param index the index of the value in the constant pool
+     * @return a printable string containing the value of the constant.
+     */
+    String getConstantValue(Descriptor d, int index) {
+        try {
+            ConstantPool.CPInfo cpInfo = constant_pool.get(index);
+
+            switch (cpInfo.getTag()) {
+                case ConstantPool.CONSTANT_Integer: {
+                    ConstantPool.CONSTANT_Integer_info info =
+                            (ConstantPool.CONSTANT_Integer_info) cpInfo;
+                    String t = d.getValue(constant_pool);
+                    if (t.equals("C")) { // character
+                        return getConstantCharValue((char) info.value);
+                    } else if (t.equals("Z")) { // boolean
+                        return String.valueOf(info.value == 1);
+                    } else { // other: assume integer
+                        return String.valueOf(info.value);
+                    }
+                }
+
+                case ConstantPool.CONSTANT_String: {
+                    ConstantPool.CONSTANT_String_info info =
+                            (ConstantPool.CONSTANT_String_info) cpInfo;
+                    return getConstantStringValue(info.getString());
+                }
+
+                default:
+                    return constantWriter.stringValue(cpInfo);
+            }
+        } catch (ConstantPoolException e) {
+            return "#" + index;
+        }
+    }
+
+    private String getConstantCharValue(char c) {
+        StringBuilder sb = new StringBuilder();
+        sb.append('\'');
+        sb.append(esc(c, '\''));
+        sb.append('\'');
+        return sb.toString();
+    }
+
+    private String getConstantStringValue(String s) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("\"");
+        for (int i = 0; i < s.length(); i++) {
+            sb.append(esc(s.charAt(i), '"'));
+        }
+        sb.append("\"");
+        return sb.toString();
+    }
+
+    private String esc(char c, char quote) {
+        if (32 <= c && c <= 126 && c != quote)
+            return String.valueOf(c);
+        else switch (c) {
+            case '\b': return "\\b";
+            case '\n': return "\\n";
+            case '\t': return "\\t";
+            case '\f': return "\\f";
+            case '\r': return "\\r";
+            case '\\': return "\\\\";
+            case '\'': return "\\'";
+            case '\"': return "\\\"";
+            default:   return String.format("\\u%04x", (int) c);
+        }
+    }
+
+    private Options options;
+    private AttributeWriter attrWriter;
+    private CodeWriter codeWriter;
+    private ConstantWriter constantWriter;
+    private ClassFile classFile;
+    private URI uri;
+    private long lastModified;
+    private String digestName;
+    private byte[] digest;
+    private int size;
+    private ConstantPool constant_pool;
+    private Method method;
+}