1/*
2 * Copyright (c) 2007, 2016, 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 */
25
26package com.sun.tools.javap;
27
28import java.net.URI;
29import java.text.DateFormat;
30import java.util.Collection;
31import java.util.Date;
32import java.util.List;
33
34import com.sun.tools.classfile.AccessFlags;
35import com.sun.tools.classfile.Attribute;
36import com.sun.tools.classfile.Attributes;
37import com.sun.tools.classfile.ClassFile;
38import com.sun.tools.classfile.Code_attribute;
39import com.sun.tools.classfile.ConstantPool;
40import com.sun.tools.classfile.ConstantPoolException;
41import com.sun.tools.classfile.ConstantValue_attribute;
42import com.sun.tools.classfile.Descriptor;
43import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
44import com.sun.tools.classfile.Exceptions_attribute;
45import com.sun.tools.classfile.Field;
46import com.sun.tools.classfile.Method;
47import com.sun.tools.classfile.Module_attribute;
48import com.sun.tools.classfile.Signature;
49import com.sun.tools.classfile.Signature_attribute;
50import com.sun.tools.classfile.SourceFile_attribute;
51import com.sun.tools.classfile.Type;
52import com.sun.tools.classfile.Type.ArrayType;
53import com.sun.tools.classfile.Type.ClassSigType;
54import com.sun.tools.classfile.Type.ClassType;
55import com.sun.tools.classfile.Type.MethodType;
56import com.sun.tools.classfile.Type.SimpleType;
57import com.sun.tools.classfile.Type.TypeParamType;
58import com.sun.tools.classfile.Type.WildcardType;
59
60import static com.sun.tools.classfile.AccessFlags.*;
61import static com.sun.tools.classfile.ConstantPool.CONSTANT_Module;
62import static com.sun.tools.classfile.ConstantPool.CONSTANT_Package;
63
64/*
65 *  The main javap class to write the contents of a class file as text.
66 *
67 *  <p><b>This is NOT part of any supported API.
68 *  If you write code that depends on this, you do so at your own risk.
69 *  This code and its internal interfaces are subject to change or
70 *  deletion without notice.</b>
71 */
72public class ClassWriter extends BasicWriter {
73    static ClassWriter instance(Context context) {
74        ClassWriter instance = context.get(ClassWriter.class);
75        if (instance == null)
76            instance = new ClassWriter(context);
77        return instance;
78    }
79
80    protected ClassWriter(Context context) {
81        super(context);
82        context.put(ClassWriter.class, this);
83        options = Options.instance(context);
84        attrWriter = AttributeWriter.instance(context);
85        codeWriter = CodeWriter.instance(context);
86        constantWriter = ConstantWriter.instance(context);
87    }
88
89    void setDigest(String name, byte[] digest) {
90        this.digestName = name;
91        this.digest = digest;
92    }
93
94    void setFile(URI uri) {
95        this.uri = uri;
96    }
97
98    void setFileSize(int size) {
99        this.size = size;
100    }
101
102    void setLastModified(long lastModified) {
103        this.lastModified = lastModified;
104    }
105
106    protected ClassFile getClassFile() {
107        return classFile;
108    }
109
110    protected void setClassFile(ClassFile cf) {
111        classFile = cf;
112        constant_pool = classFile.constant_pool;
113    }
114
115    protected Method getMethod() {
116        return method;
117    }
118
119    protected void setMethod(Method m) {
120        method = m;
121    }
122
123    public void write(ClassFile cf) {
124        setClassFile(cf);
125
126        if (options.sysInfo || options.verbose) {
127            if (uri != null) {
128                if (uri.getScheme().equals("file"))
129                    println("Classfile " + uri.getPath());
130                else
131                    println("Classfile " + uri);
132            }
133            indent(+1);
134            if (lastModified != -1) {
135                Date lm = new Date(lastModified);
136                DateFormat df = DateFormat.getDateInstance();
137                if (size > 0) {
138                    println("Last modified " + df.format(lm) + "; size " + size + " bytes");
139                } else {
140                    println("Last modified " + df.format(lm));
141                }
142            } else if (size > 0) {
143                println("Size " + size + " bytes");
144            }
145            if (digestName != null && digest != null) {
146                StringBuilder sb = new StringBuilder();
147                for (byte b: digest)
148                    sb.append(String.format("%02x", b));
149                println(digestName + " checksum " + sb);
150            }
151        }
152
153        Attribute sfa = cf.getAttribute(Attribute.SourceFile);
154        if (sfa instanceof SourceFile_attribute) {
155            println("Compiled from \"" + getSourceFile((SourceFile_attribute) sfa) + "\"");
156        }
157
158        if (options.sysInfo || options.verbose) {
159            indent(-1);
160        }
161
162        AccessFlags flags = cf.access_flags;
163        writeModifiers(flags.getClassModifiers());
164
165        if (classFile.access_flags.is(AccessFlags.ACC_MODULE)) {
166            Attribute attr = classFile.attributes.get(Attribute.Module);
167            if (attr instanceof Module_attribute) {
168                Module_attribute modAttr = (Module_attribute) attr;
169                String name;
170                try {
171                    // FIXME: compatibility code
172                    if (constant_pool.get(modAttr.module_name).getTag() == CONSTANT_Module) {
173                        name = getJavaName(constant_pool.getModuleInfo(modAttr.module_name).getName());
174                    } else {
175                        name = getJavaName(constant_pool.getUTF8Value(modAttr.module_name));
176                    }
177                } catch (ConstantPoolException e) {
178                    name = report(e);
179                }
180                if ((modAttr.module_flags & Module_attribute.ACC_OPEN) != 0) {
181                    print("open ");
182                }
183                print("module ");
184                print(name);
185                if (modAttr.module_version_index != 0) {
186                    print("@");
187                    print(getUTF8Value(modAttr.module_version_index));
188                }
189            } else {
190                // fallback for malformed class files
191                print("class ");
192                print(getJavaName(classFile));
193            }
194        } else {
195            if (classFile.isClass())
196                print("class ");
197            else if (classFile.isInterface())
198                print("interface ");
199
200            print(getJavaName(classFile));
201        }
202
203        Signature_attribute sigAttr = getSignature(cf.attributes);
204        if (sigAttr == null) {
205            // use info from class file header
206            if (classFile.isClass() && classFile.super_class != 0 ) {
207                String sn = getJavaSuperclassName(cf);
208                if (!sn.equals("java.lang.Object")) {
209                    print(" extends ");
210                    print(sn);
211                }
212            }
213            for (int i = 0; i < classFile.interfaces.length; i++) {
214                print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ",");
215                print(getJavaInterfaceName(classFile, i));
216            }
217        } else {
218            try {
219                Type t = sigAttr.getParsedSignature().getType(constant_pool);
220                JavaTypePrinter p = new JavaTypePrinter(classFile.isInterface());
221                // The signature parser cannot disambiguate between a
222                // FieldType and a ClassSignatureType that only contains a superclass type.
223                if (t instanceof Type.ClassSigType) {
224                    print(p.print(t));
225                } else if (options.verbose || !t.isObject()) {
226                    print(" extends ");
227                    print(p.print(t));
228                }
229            } catch (ConstantPoolException e) {
230                print(report(e));
231            }
232        }
233
234        if (options.verbose) {
235            println();
236            indent(+1);
237            println("minor version: " + cf.minor_version);
238            println("major version: " + cf.major_version);
239            writeList(String.format("flags: (0x%04x) ", flags.flags), flags.getClassFlags(), "\n");
240            print("this_class: #" + cf.this_class);
241            if (cf.this_class != 0) {
242                tab();
243                print("// " + constantWriter.stringValue(cf.this_class));
244            }
245            println();
246            print("super_class: #" + cf.super_class);
247            if (cf.super_class != 0) {
248                tab();
249                print("// " + constantWriter.stringValue(cf.super_class));
250            }
251            println();
252            print("interfaces: " + cf.interfaces.length);
253            print(", fields: " + cf.fields.length);
254            print(", methods: " + cf.methods.length);
255            println(", attributes: " + cf.attributes.attrs.length);
256            indent(-1);
257            constantWriter.writeConstantPool();
258        } else {
259            print(" ");
260        }
261
262        println("{");
263        indent(+1);
264        if (flags.is(AccessFlags.ACC_MODULE) && !options.verbose) {
265            writeDirectives();
266        }
267        writeFields();
268        writeMethods();
269        indent(-1);
270        println("}");
271
272        if (options.verbose) {
273            attrWriter.write(cf, cf.attributes, constant_pool);
274        }
275    }
276    // where
277        class JavaTypePrinter implements Type.Visitor<StringBuilder,StringBuilder> {
278            boolean isInterface;
279
280            JavaTypePrinter(boolean isInterface) {
281                this.isInterface = isInterface;
282            }
283
284            String print(Type t) {
285                return t.accept(this, new StringBuilder()).toString();
286            }
287
288            String printTypeArgs(List<? extends TypeParamType> typeParamTypes) {
289                StringBuilder builder = new StringBuilder();
290                appendIfNotEmpty(builder, "<", typeParamTypes, "> ");
291                return builder.toString();
292            }
293
294            @Override
295            public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) {
296                sb.append(getJavaName(type.name));
297                return sb;
298            }
299
300            @Override
301            public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) {
302                append(sb, type.elemType);
303                sb.append("[]");
304                return sb;
305            }
306
307            @Override
308            public StringBuilder visitMethodType(MethodType type, StringBuilder sb) {
309                appendIfNotEmpty(sb, "<", type.typeParamTypes, "> ");
310                append(sb, type.returnType);
311                append(sb, " (", type.paramTypes, ")");
312                appendIfNotEmpty(sb, " throws ", type.throwsTypes, "");
313                return sb;
314            }
315
316            @Override
317            public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) {
318                appendIfNotEmpty(sb, "<", type.typeParamTypes, ">");
319                if (isInterface) {
320                    appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, "");
321                } else {
322                    if (type.superclassType != null
323                            && (options.verbose || !type.superclassType.isObject())) {
324                        sb.append(" extends ");
325                        append(sb, type.superclassType);
326                    }
327                    appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, "");
328                }
329                return sb;
330            }
331
332            @Override
333            public StringBuilder visitClassType(ClassType type, StringBuilder sb) {
334                if (type.outerType != null) {
335                    append(sb, type.outerType);
336                    sb.append(".");
337                }
338                sb.append(getJavaName(type.name));
339                appendIfNotEmpty(sb, "<", type.typeArgs, ">");
340                return sb;
341            }
342
343            @Override
344            public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) {
345                sb.append(type.name);
346                String sep = " extends ";
347                if (type.classBound != null
348                        && (options.verbose || !type.classBound.isObject())) {
349                    sb.append(sep);
350                    append(sb, type.classBound);
351                    sep = " & ";
352                }
353                if (type.interfaceBounds != null) {
354                    for (Type bound: type.interfaceBounds) {
355                        sb.append(sep);
356                        append(sb, bound);
357                        sep = " & ";
358                    }
359                }
360                return sb;
361            }
362
363            @Override
364            public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) {
365                switch (type.kind) {
366                    case UNBOUNDED:
367                        sb.append("?");
368                        break;
369                    case EXTENDS:
370                        sb.append("? extends ");
371                        append(sb, type.boundType);
372                        break;
373                    case SUPER:
374                        sb.append("? super ");
375                        append(sb, type.boundType);
376                        break;
377                    default:
378                        throw new AssertionError();
379                }
380                return sb;
381            }
382
383            private void append(StringBuilder sb, Type t) {
384                t.accept(this, sb);
385            }
386
387            private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
388                sb.append(prefix);
389                String sep = "";
390                for (Type t: list) {
391                    sb.append(sep);
392                    append(sb, t);
393                    sep = ", ";
394                }
395                sb.append(suffix);
396            }
397
398            private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
399                if (!isEmpty(list))
400                    append(sb, prefix, list, suffix);
401            }
402
403            private boolean isEmpty(List<? extends Type> list) {
404                return (list == null || list.isEmpty());
405            }
406        }
407
408    protected void writeFields() {
409        for (Field f: classFile.fields) {
410            writeField(f);
411        }
412    }
413
414    protected void writeField(Field f) {
415        if (!options.checkAccess(f.access_flags))
416            return;
417
418        AccessFlags flags = f.access_flags;
419        writeModifiers(flags.getFieldModifiers());
420        Signature_attribute sigAttr = getSignature(f.attributes);
421        if (sigAttr == null)
422            print(getJavaFieldType(f.descriptor));
423        else {
424            try {
425                Type t = sigAttr.getParsedSignature().getType(constant_pool);
426                print(getJavaName(t.toString()));
427            } catch (ConstantPoolException e) {
428                // report error?
429                // fall back on non-generic descriptor
430                print(getJavaFieldType(f.descriptor));
431            }
432        }
433        print(" ");
434        print(getFieldName(f));
435        if (options.showConstants) {
436            Attribute a = f.attributes.get(Attribute.ConstantValue);
437            if (a instanceof ConstantValue_attribute) {
438                print(" = ");
439                ConstantValue_attribute cv = (ConstantValue_attribute) a;
440                print(getConstantValue(f.descriptor, cv.constantvalue_index));
441            }
442        }
443        print(";");
444        println();
445
446        indent(+1);
447
448        boolean showBlank = false;
449
450        if (options.showDescriptors)
451            println("descriptor: " + getValue(f.descriptor));
452
453        if (options.verbose)
454            writeList(String.format("flags: (0x%04x) ", flags.flags), flags.getFieldFlags(), "\n");
455
456        if (options.showAllAttrs) {
457            for (Attribute attr: f.attributes)
458                attrWriter.write(f, attr, constant_pool);
459            showBlank = true;
460        }
461
462        indent(-1);
463
464        if (showBlank || options.showDisassembled || options.showLineAndLocalVariableTables)
465            println();
466    }
467
468    protected void writeMethods() {
469        for (Method m: classFile.methods)
470            writeMethod(m);
471        setPendingNewline(false);
472    }
473
474    protected void writeMethod(Method m) {
475        if (!options.checkAccess(m.access_flags))
476            return;
477
478        method = m;
479
480        AccessFlags flags = m.access_flags;
481
482        Descriptor d;
483        Type.MethodType methodType;
484        List<? extends Type> methodExceptions;
485
486        Signature_attribute sigAttr = getSignature(m.attributes);
487        if (sigAttr == null) {
488            d = m.descriptor;
489            methodType = null;
490            methodExceptions = null;
491        } else {
492            Signature methodSig = sigAttr.getParsedSignature();
493            d = methodSig;
494            try {
495                methodType = (Type.MethodType) methodSig.getType(constant_pool);
496                methodExceptions = methodType.throwsTypes;
497                if (methodExceptions != null && methodExceptions.isEmpty())
498                    methodExceptions = null;
499            } catch (ConstantPoolException e) {
500                // report error?
501                // fall back on standard descriptor
502                methodType = null;
503                methodExceptions = null;
504            }
505        }
506
507        writeModifiers(flags.getMethodModifiers());
508        if (methodType != null) {
509            print(new JavaTypePrinter(false).printTypeArgs(methodType.typeParamTypes));
510        }
511        switch (getName(m)) {
512            case "<init>":
513                print(getJavaName(classFile));
514                print(getJavaParameterTypes(d, flags));
515                break;
516            case "<clinit>":
517                print("{}");
518                break;
519            default:
520                print(getJavaReturnType(d));
521                print(" ");
522                print(getName(m));
523                print(getJavaParameterTypes(d, flags));
524                break;
525        }
526
527        Attribute e_attr = m.attributes.get(Attribute.Exceptions);
528        if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
529            if (e_attr instanceof Exceptions_attribute) {
530                Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
531                print(" throws ");
532                if (methodExceptions != null) { // use generic list if available
533                    writeList("", methodExceptions, "");
534                } else {
535                    for (int i = 0; i < exceptions.number_of_exceptions; i++) {
536                        if (i > 0)
537                            print(", ");
538                        print(getJavaException(exceptions, i));
539                    }
540                }
541            } else {
542                report("Unexpected or invalid value for Exceptions attribute");
543            }
544        }
545
546        println(";");
547
548        indent(+1);
549
550        if (options.showDescriptors) {
551            println("descriptor: " + getValue(m.descriptor));
552        }
553
554        if (options.verbose) {
555            writeList(String.format("flags: (0x%04x) ", flags.flags), flags.getMethodFlags(), "\n");
556        }
557
558        Code_attribute code = null;
559        Attribute c_attr = m.attributes.get(Attribute.Code);
560        if (c_attr != null) {
561            if (c_attr instanceof Code_attribute)
562                code = (Code_attribute) c_attr;
563            else
564                report("Unexpected or invalid value for Code attribute");
565        }
566
567        if (options.showAllAttrs) {
568            Attribute[] attrs = m.attributes.attrs;
569            for (Attribute attr: attrs)
570                attrWriter.write(m, attr, constant_pool);
571        } else if (code != null) {
572            if (options.showDisassembled) {
573                println("Code:");
574                codeWriter.writeInstrs(code);
575                codeWriter.writeExceptionTable(code);
576            }
577
578            if (options.showLineAndLocalVariableTables) {
579                attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
580                attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
581            }
582        }
583
584        indent(-1);
585
586        // set pendingNewline to write a newline before the next method (if any)
587        // if a separator is desired
588        setPendingNewline(
589                options.showDisassembled ||
590                options.showAllAttrs ||
591                options.showDescriptors ||
592                options.showLineAndLocalVariableTables ||
593                options.verbose);
594    }
595
596    void writeModifiers(Collection<String> items) {
597        for (Object item: items) {
598            print(item);
599            print(" ");
600        }
601    }
602
603    void writeDirectives() {
604        Attribute attr = classFile.attributes.get(Attribute.Module);
605        if (!(attr instanceof Module_attribute))
606            return;
607
608        Module_attribute m = (Module_attribute) attr;
609        for (Module_attribute.RequiresEntry entry: m.requires) {
610            print("requires");
611            if ((entry.requires_flags & Module_attribute.ACC_STATIC_PHASE) != 0)
612                print(" static");
613            if ((entry.requires_flags & Module_attribute.ACC_TRANSITIVE) != 0)
614                print(" transitive");
615            print(" ");
616            String mname;
617            try {
618                mname = getModuleName(entry.requires_index);
619            } catch (ConstantPoolException e) {
620                mname = report(e);
621            }
622            print(mname);
623            println(";");
624        }
625
626        for (Module_attribute.ExportsEntry entry: m.exports) {
627            print("exports");
628            print(" ");
629            String pname;
630            try {
631                pname = getPackageName(entry.exports_index).replace('/', '.');
632            } catch (ConstantPoolException e) {
633                pname = report(e);
634            }
635            print(pname);
636            boolean first = true;
637            for (int i: entry.exports_to_index) {
638                String mname;
639                try {
640                    mname = getModuleName(i);
641                } catch (ConstantPoolException e) {
642                    mname = report(e);
643                }
644                if (first) {
645                    println(" to");
646                    indent(+1);
647                    first = false;
648                } else {
649                    println(",");
650                }
651                print(mname);
652            }
653            println(";");
654            if (!first)
655                indent(-1);
656        }
657
658        for (Module_attribute.OpensEntry entry: m.opens) {
659            print("opens");
660            print(" ");
661            String pname;
662            try {
663                pname = getPackageName(entry.opens_index).replace('/', '.');
664            } catch (ConstantPoolException e) {
665                pname = report(e);
666            }
667            print(pname);
668            boolean first = true;
669            for (int i: entry.opens_to_index) {
670                String mname;
671                try {
672                    mname = getModuleName(i);
673                } catch (ConstantPoolException e) {
674                    mname = report(e);
675                }
676                if (first) {
677                    println(" to");
678                    indent(+1);
679                    first = false;
680                } else {
681                    println(",");
682                }
683                print(mname);
684            }
685            println(";");
686            if (!first)
687                indent(-1);
688        }
689
690        for (int entry: m.uses_index) {
691            print("uses ");
692            print(getClassName(entry).replace('/', '.'));
693            println(";");
694        }
695
696        for (Module_attribute.ProvidesEntry entry: m.provides) {
697            print("provides  ");
698            print(getClassName(entry.provides_index).replace('/', '.'));
699            boolean first = true;
700            for (int i: entry.with_index) {
701                if (first) {
702                    println(" with");
703                    indent(+1);
704                    first = false;
705                } else {
706                    println(",");
707                }
708                print(getClassName(i).replace('/', '.'));
709            }
710            println(";");
711            if (!first)
712                indent(-1);
713        }
714    }
715
716    String getModuleName(int index) throws ConstantPoolException {
717        if (constant_pool.get(index).getTag() == CONSTANT_Module) {
718            return constant_pool.getModuleInfo(index).getName();
719        } else {
720            return constant_pool.getUTF8Value(index);
721        }
722    }
723
724    String getPackageName(int index) throws ConstantPoolException {
725        if (constant_pool.get(index).getTag() == CONSTANT_Package) {
726            return constant_pool.getPackageInfo(index).getName();
727        } else {
728            return constant_pool.getUTF8Value(index);
729        }
730    }
731
732    String getUTF8Value(int index) {
733        try {
734            return classFile.constant_pool.getUTF8Value(index);
735        } catch (ConstantPoolException e) {
736            return report(e);
737        }
738    }
739
740    String getClassName(int index) {
741        try {
742            return classFile.constant_pool.getClassInfo(index).getName();
743        } catch (ConstantPoolException e) {
744            return report(e);
745        }
746    }
747
748    void writeList(String prefix, Collection<?> items, String suffix) {
749        print(prefix);
750        String sep = "";
751        for (Object item: items) {
752            print(sep);
753            print(item);
754            sep = ", ";
755        }
756        print(suffix);
757    }
758
759    void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
760        if (items != null && items.size() > 0)
761            writeList(prefix, items, suffix);
762    }
763
764    Signature_attribute getSignature(Attributes attributes) {
765        return (Signature_attribute) attributes.get(Attribute.Signature);
766    }
767
768    String adjustVarargs(AccessFlags flags, String params) {
769        if (flags.is(ACC_VARARGS)) {
770            int i = params.lastIndexOf("[]");
771            if (i > 0)
772                return params.substring(0, i) + "..." + params.substring(i+2);
773        }
774
775        return params;
776    }
777
778    String getJavaName(ClassFile cf) {
779        try {
780            return getJavaName(cf.getName());
781        } catch (ConstantPoolException e) {
782            return report(e);
783        }
784    }
785
786    String getJavaSuperclassName(ClassFile cf) {
787        try {
788            return getJavaName(cf.getSuperclassName());
789        } catch (ConstantPoolException e) {
790            return report(e);
791        }
792    }
793
794    String getJavaInterfaceName(ClassFile cf, int index) {
795        try {
796            return getJavaName(cf.getInterfaceName(index));
797        } catch (ConstantPoolException e) {
798            return report(e);
799        }
800    }
801
802    String getJavaFieldType(Descriptor d) {
803        try {
804            return getJavaName(d.getFieldType(constant_pool));
805        } catch (ConstantPoolException e) {
806            return report(e);
807        } catch (InvalidDescriptor e) {
808            return report(e);
809        }
810    }
811
812    String getJavaReturnType(Descriptor d) {
813        try {
814            return getJavaName(d.getReturnType(constant_pool));
815        } catch (ConstantPoolException e) {
816            return report(e);
817        } catch (InvalidDescriptor e) {
818            return report(e);
819        }
820    }
821
822    String getJavaParameterTypes(Descriptor d, AccessFlags flags) {
823        try {
824            return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool)));
825        } catch (ConstantPoolException e) {
826            return report(e);
827        } catch (InvalidDescriptor e) {
828            return report(e);
829        }
830    }
831
832    String getJavaException(Exceptions_attribute attr, int index) {
833        try {
834            return getJavaName(attr.getException(index, constant_pool));
835        } catch (ConstantPoolException e) {
836            return report(e);
837        }
838    }
839
840    String getValue(Descriptor d) {
841        try {
842            return d.getValue(constant_pool);
843        } catch (ConstantPoolException e) {
844            return report(e);
845        }
846    }
847
848    String getFieldName(Field f) {
849        try {
850            return f.getName(constant_pool);
851        } catch (ConstantPoolException e) {
852            return report(e);
853        }
854    }
855
856    String getName(Method m) {
857        try {
858            return m.getName(constant_pool);
859        } catch (ConstantPoolException e) {
860            return report(e);
861        }
862    }
863
864    static String getJavaName(String name) {
865        return name.replace('/', '.');
866    }
867
868    String getSourceFile(SourceFile_attribute attr) {
869        try {
870            return attr.getSourceFile(constant_pool);
871        } catch (ConstantPoolException e) {
872            return report(e);
873        }
874    }
875
876    /**
877     * Get the value of an entry in the constant pool as a Java constant.
878     * Characters and booleans are represented by CONSTANT_Intgere entries.
879     * Character and string values are processed to escape characters outside
880     * the basic printable ASCII set.
881     * @param d the descriptor, giving the expected type of the constant
882     * @param index the index of the value in the constant pool
883     * @return a printable string containing the value of the constant.
884     */
885    String getConstantValue(Descriptor d, int index) {
886        try {
887            ConstantPool.CPInfo cpInfo = constant_pool.get(index);
888
889            switch (cpInfo.getTag()) {
890                case ConstantPool.CONSTANT_Integer: {
891                    ConstantPool.CONSTANT_Integer_info info =
892                            (ConstantPool.CONSTANT_Integer_info) cpInfo;
893                    String t = d.getValue(constant_pool);
894                    switch (t) {
895                        case "C":
896                            // character
897                            return getConstantCharValue((char) info.value);
898                        case "Z":
899                            // boolean
900                            return String.valueOf(info.value == 1);
901                        default:
902                            // other: assume integer
903                            return String.valueOf(info.value);
904                    }
905                }
906
907                case ConstantPool.CONSTANT_String: {
908                    ConstantPool.CONSTANT_String_info info =
909                            (ConstantPool.CONSTANT_String_info) cpInfo;
910                    return getConstantStringValue(info.getString());
911                }
912
913                default:
914                    return constantWriter.stringValue(cpInfo);
915            }
916        } catch (ConstantPoolException e) {
917            return "#" + index;
918        }
919    }
920
921    private String getConstantCharValue(char c) {
922        StringBuilder sb = new StringBuilder();
923        sb.append('\'');
924        sb.append(esc(c, '\''));
925        sb.append('\'');
926        return sb.toString();
927    }
928
929    private String getConstantStringValue(String s) {
930        StringBuilder sb = new StringBuilder();
931        sb.append("\"");
932        for (int i = 0; i < s.length(); i++) {
933            sb.append(esc(s.charAt(i), '"'));
934        }
935        sb.append("\"");
936        return sb.toString();
937    }
938
939    private String esc(char c, char quote) {
940        if (32 <= c && c <= 126 && c != quote && c != '\\')
941            return String.valueOf(c);
942        else switch (c) {
943            case '\b': return "\\b";
944            case '\n': return "\\n";
945            case '\t': return "\\t";
946            case '\f': return "\\f";
947            case '\r': return "\\r";
948            case '\\': return "\\\\";
949            case '\'': return "\\'";
950            case '\"': return "\\\"";
951            default:   return String.format("\\u%04x", (int) c);
952        }
953    }
954
955    private final Options options;
956    private final AttributeWriter attrWriter;
957    private final CodeWriter codeWriter;
958    private final ConstantWriter constantWriter;
959    private ClassFile classFile;
960    private URI uri;
961    private long lastModified;
962    private String digestName;
963    private byte[] digest;
964    private int size;
965    private ConstantPool constant_pool;
966    private Method method;
967}
968