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    }