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