ClassWriter.java revision 3294:9adfb22ff08f
1/*
2 * Copyright (c) 2007, 2015, 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.DescriptorException;
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        String name = getJavaName(classFile);
161        AccessFlags flags = cf.access_flags;
162
163        writeModifiers(flags.getClassModifiers());
164
165        if (classFile.access_flags.is(AccessFlags.ACC_MODULE) && name.endsWith(".module-info")) {
166            print("module ");
167            print(name.replace(".module-info", ""));
168        } else {
169            if (classFile.isClass())
170                print("class ");
171            else if (classFile.isInterface())
172                print("interface ");
173
174            print(name);
175        }
176
177        Signature_attribute sigAttr = getSignature(cf.attributes);
178        if (sigAttr == null) {
179            // use info from class file header
180            if (classFile.isClass() && classFile.super_class != 0 ) {
181                String sn = getJavaSuperclassName(cf);
182                if (!sn.equals("java.lang.Object")) {
183                    print(" extends ");
184                    print(sn);
185                }
186            }
187            for (int i = 0; i < classFile.interfaces.length; i++) {
188                print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ",");
189                print(getJavaInterfaceName(classFile, i));
190            }
191        } else {
192            try {
193                Type t = sigAttr.getParsedSignature().getType(constant_pool);
194                JavaTypePrinter p = new JavaTypePrinter(classFile.isInterface());
195                // The signature parser cannot disambiguate between a
196                // FieldType and a ClassSignatureType that only contains a superclass type.
197                if (t instanceof Type.ClassSigType) {
198                    print(p.print(t));
199                } else if (options.verbose || !t.isObject()) {
200                    print(" extends ");
201                    print(p.print(t));
202                }
203            } catch (ConstantPoolException e) {
204                print(report(e));
205            }
206        }
207
208        if (options.verbose) {
209            println();
210            indent(+1);
211            println("minor version: " + cf.minor_version);
212            println("major version: " + cf.major_version);
213            writeList("flags: ", flags.getClassFlags(), "\n");
214            indent(-1);
215            constantWriter.writeConstantPool();
216        } else {
217            print(" ");
218        }
219
220        println("{");
221        indent(+1);
222        if (flags.is(AccessFlags.ACC_MODULE) && !options.verbose) {
223            writeDirectives();
224        }
225        writeFields();
226        writeMethods();
227        indent(-1);
228        println("}");
229
230        if (options.verbose) {
231            attrWriter.write(cf, cf.attributes, constant_pool);
232        }
233    }
234    // where
235        class JavaTypePrinter implements Type.Visitor<StringBuilder,StringBuilder> {
236            boolean isInterface;
237
238            JavaTypePrinter(boolean isInterface) {
239                this.isInterface = isInterface;
240            }
241
242            String print(Type t) {
243                return t.accept(this, new StringBuilder()).toString();
244            }
245
246            String printTypeArgs(List<? extends TypeParamType> typeParamTypes) {
247                StringBuilder builder = new StringBuilder();
248                appendIfNotEmpty(builder, "<", typeParamTypes, "> ");
249                return builder.toString();
250            }
251
252            public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) {
253                sb.append(getJavaName(type.name));
254                return sb;
255            }
256
257            public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) {
258                append(sb, type.elemType);
259                sb.append("[]");
260                return sb;
261            }
262
263            public StringBuilder visitMethodType(MethodType type, StringBuilder sb) {
264                appendIfNotEmpty(sb, "<", type.typeParamTypes, "> ");
265                append(sb, type.returnType);
266                append(sb, " (", type.paramTypes, ")");
267                appendIfNotEmpty(sb, " throws ", type.throwsTypes, "");
268                return sb;
269            }
270
271            public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) {
272                appendIfNotEmpty(sb, "<", type.typeParamTypes, ">");
273                if (isInterface) {
274                    appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, "");
275                } else {
276                    if (type.superclassType != null
277                            && (options.verbose || !type.superclassType.isObject())) {
278                        sb.append(" extends ");
279                        append(sb, type.superclassType);
280                    }
281                    appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, "");
282                }
283                return sb;
284            }
285
286            public StringBuilder visitClassType(ClassType type, StringBuilder sb) {
287                if (type.outerType != null) {
288                    append(sb, type.outerType);
289                    sb.append(".");
290                }
291                sb.append(getJavaName(type.name));
292                appendIfNotEmpty(sb, "<", type.typeArgs, ">");
293                return sb;
294            }
295
296            public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) {
297                sb.append(type.name);
298                String sep = " extends ";
299                if (type.classBound != null
300                        && (options.verbose || !type.classBound.isObject())) {
301                    sb.append(sep);
302                    append(sb, type.classBound);
303                    sep = " & ";
304                }
305                if (type.interfaceBounds != null) {
306                    for (Type bound: type.interfaceBounds) {
307                        sb.append(sep);
308                        append(sb, bound);
309                        sep = " & ";
310                    }
311                }
312                return sb;
313            }
314
315            public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) {
316                switch (type.kind) {
317                    case UNBOUNDED:
318                        sb.append("?");
319                        break;
320                    case EXTENDS:
321                        sb.append("? extends ");
322                        append(sb, type.boundType);
323                        break;
324                    case SUPER:
325                        sb.append("? super ");
326                        append(sb, type.boundType);
327                        break;
328                    default:
329                        throw new AssertionError();
330                }
331                return sb;
332            }
333
334            private void append(StringBuilder sb, Type t) {
335                t.accept(this, sb);
336            }
337
338            private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
339                sb.append(prefix);
340                String sep = "";
341                for (Type t: list) {
342                    sb.append(sep);
343                    append(sb, t);
344                    sep = ", ";
345                }
346                sb.append(suffix);
347            }
348
349            private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
350                if (!isEmpty(list))
351                    append(sb, prefix, list, suffix);
352            }
353
354            private boolean isEmpty(List<? extends Type> list) {
355                return (list == null || list.isEmpty());
356            }
357        }
358
359    protected void writeFields() {
360        for (Field f: classFile.fields) {
361            writeField(f);
362        }
363    }
364
365    protected void writeField(Field f) {
366        if (!options.checkAccess(f.access_flags))
367            return;
368
369        AccessFlags flags = f.access_flags;
370        writeModifiers(flags.getFieldModifiers());
371        Signature_attribute sigAttr = getSignature(f.attributes);
372        if (sigAttr == null)
373            print(getJavaFieldType(f.descriptor));
374        else {
375            try {
376                Type t = sigAttr.getParsedSignature().getType(constant_pool);
377                print(getJavaName(t.toString()));
378            } catch (ConstantPoolException e) {
379                // report error?
380                // fall back on non-generic descriptor
381                print(getJavaFieldType(f.descriptor));
382            }
383        }
384        print(" ");
385        print(getFieldName(f));
386        if (options.showConstants) {
387            Attribute a = f.attributes.get(Attribute.ConstantValue);
388            if (a instanceof ConstantValue_attribute) {
389                print(" = ");
390                ConstantValue_attribute cv = (ConstantValue_attribute) a;
391                print(getConstantValue(f.descriptor, cv.constantvalue_index));
392            }
393        }
394        print(";");
395        println();
396
397        indent(+1);
398
399        boolean showBlank = false;
400
401        if (options.showDescriptors)
402            println("descriptor: " + getValue(f.descriptor));
403
404        if (options.verbose)
405            writeList("flags: ", flags.getFieldFlags(), "\n");
406
407        if (options.showAllAttrs) {
408            for (Attribute attr: f.attributes)
409                attrWriter.write(f, attr, constant_pool);
410            showBlank = true;
411        }
412
413        indent(-1);
414
415        if (showBlank || options.showDisassembled || options.showLineAndLocalVariableTables)
416            println();
417    }
418
419    protected void writeMethods() {
420        for (Method m: classFile.methods)
421            writeMethod(m);
422        setPendingNewline(false);
423    }
424
425    protected void writeMethod(Method m) {
426        if (!options.checkAccess(m.access_flags))
427            return;
428
429        method = m;
430
431        AccessFlags flags = m.access_flags;
432
433        Descriptor d;
434        Type.MethodType methodType;
435        List<? extends Type> methodExceptions;
436
437        Signature_attribute sigAttr = getSignature(m.attributes);
438        if (sigAttr == null) {
439            d = m.descriptor;
440            methodType = null;
441            methodExceptions = null;
442        } else {
443            Signature methodSig = sigAttr.getParsedSignature();
444            d = methodSig;
445            try {
446                methodType = (Type.MethodType) methodSig.getType(constant_pool);
447                methodExceptions = methodType.throwsTypes;
448                if (methodExceptions != null && methodExceptions.isEmpty())
449                    methodExceptions = null;
450            } catch (ConstantPoolException e) {
451                // report error?
452                // fall back on standard descriptor
453                methodType = null;
454                methodExceptions = null;
455            }
456        }
457
458        writeModifiers(flags.getMethodModifiers());
459        if (methodType != null) {
460            print(new JavaTypePrinter(false).printTypeArgs(methodType.typeParamTypes));
461        }
462        if (getName(m).equals("<init>")) {
463            print(getJavaName(classFile));
464            print(getJavaParameterTypes(d, flags));
465        } else if (getName(m).equals("<clinit>")) {
466            print("{}");
467        } else {
468            print(getJavaReturnType(d));
469            print(" ");
470            print(getName(m));
471            print(getJavaParameterTypes(d, flags));
472        }
473
474        Attribute e_attr = m.attributes.get(Attribute.Exceptions);
475        if (e_attr != null) { // if there are generic exceptions, there must be erased exceptions
476            if (e_attr instanceof Exceptions_attribute) {
477                Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
478                print(" throws ");
479                if (methodExceptions != null) { // use generic list if available
480                    writeList("", methodExceptions, "");
481                } else {
482                    for (int i = 0; i < exceptions.number_of_exceptions; i++) {
483                        if (i > 0)
484                            print(", ");
485                        print(getJavaException(exceptions, i));
486                    }
487                }
488            } else {
489                report("Unexpected or invalid value for Exceptions attribute");
490            }
491        }
492
493        println(";");
494
495        indent(+1);
496
497        if (options.showDescriptors) {
498            println("descriptor: " + getValue(m.descriptor));
499        }
500
501        if (options.verbose) {
502            writeList("flags: ", flags.getMethodFlags(), "\n");
503        }
504
505        Code_attribute code = null;
506        Attribute c_attr = m.attributes.get(Attribute.Code);
507        if (c_attr != null) {
508            if (c_attr instanceof Code_attribute)
509                code = (Code_attribute) c_attr;
510            else
511                report("Unexpected or invalid value for Code attribute");
512        }
513
514        if (options.showAllAttrs) {
515            Attribute[] attrs = m.attributes.attrs;
516            for (Attribute attr: attrs)
517                attrWriter.write(m, attr, constant_pool);
518        } else if (code != null) {
519            if (options.showDisassembled) {
520                println("Code:");
521                codeWriter.writeInstrs(code);
522                codeWriter.writeExceptionTable(code);
523            }
524
525            if (options.showLineAndLocalVariableTables) {
526                attrWriter.write(code, code.attributes.get(Attribute.LineNumberTable), constant_pool);
527                attrWriter.write(code, code.attributes.get(Attribute.LocalVariableTable), constant_pool);
528            }
529        }
530
531        indent(-1);
532
533        // set pendingNewline to write a newline before the next method (if any)
534        // if a separator is desired
535        setPendingNewline(
536                options.showDisassembled ||
537                options.showAllAttrs ||
538                options.showDescriptors ||
539                options.showLineAndLocalVariableTables ||
540                options.verbose);
541    }
542
543    void writeModifiers(Collection<String> items) {
544        for (Object item: items) {
545            print(item);
546            print(" ");
547        }
548    }
549
550    void writeDirectives() {
551        Attribute attr = classFile.attributes.get(Attribute.Module);
552        if (!(attr instanceof Module_attribute))
553            return;
554
555        Module_attribute m = (Module_attribute) attr;
556        for (Module_attribute.RequiresEntry entry: m.requires) {
557            print("requires");
558            if ((entry.requires_flags & Module_attribute.ACC_PUBLIC) != 0)
559                print(" public");
560            print(" ");
561            print(getUTF8Value(entry.requires_index).replace('/', '.'));
562            println(";");
563        }
564
565        for (Module_attribute.ExportsEntry entry: m.exports) {
566            print("exports ");
567            print(getUTF8Value(entry.exports_index).replace('/', '.'));
568            boolean first = true;
569            for (int i: entry.exports_to_index) {
570                String mname;
571                try {
572                    mname = classFile.constant_pool.getUTF8Value(i).replace('/', '.');
573                } catch (ConstantPoolException e) {
574                    mname = report(e);
575                }
576                if (first) {
577                    println(" to");
578                    indent(+1);
579                    first = false;
580                } else {
581                    println(",");
582                }
583                print(mname);
584            }
585            println(";");
586            if (!first)
587                indent(-1);
588        }
589
590        for (int entry: m.uses_index) {
591            print("uses ");
592            print(getClassName(entry).replace('/', '.'));
593            println(";");
594        }
595
596        for (Module_attribute.ProvidesEntry entry: m.provides) {
597            print("provides ");
598            print(getClassName(entry.provides_index).replace('/', '.'));
599            println(" with");
600            indent(+1);
601            print(getClassName(entry.with_index).replace('/', '.'));
602            println(";");
603            indent(-1);
604        }
605    }
606
607    String getUTF8Value(int index) {
608        try {
609            return classFile.constant_pool.getUTF8Value(index);
610        } catch (ConstantPoolException e) {
611            return report(e);
612        }
613    }
614
615    String getClassName(int index) {
616        try {
617            return classFile.constant_pool.getClassInfo(index).getName();
618        } catch (ConstantPoolException e) {
619            return report(e);
620        }
621    }
622
623    void writeList(String prefix, Collection<?> items, String suffix) {
624        print(prefix);
625        String sep = "";
626        for (Object item: items) {
627            print(sep);
628            print(item);
629            sep = ", ";
630        }
631        print(suffix);
632    }
633
634    void writeListIfNotEmpty(String prefix, List<?> items, String suffix) {
635        if (items != null && items.size() > 0)
636            writeList(prefix, items, suffix);
637    }
638
639    Signature_attribute getSignature(Attributes attributes) {
640        return (Signature_attribute) attributes.get(Attribute.Signature);
641    }
642
643    String adjustVarargs(AccessFlags flags, String params) {
644        if (flags.is(ACC_VARARGS)) {
645            int i = params.lastIndexOf("[]");
646            if (i > 0)
647                return params.substring(0, i) + "..." + params.substring(i+2);
648        }
649
650        return params;
651    }
652
653    String getJavaName(ClassFile cf) {
654        try {
655            return getJavaName(cf.getName());
656        } catch (ConstantPoolException e) {
657            return report(e);
658        }
659    }
660
661    String getJavaSuperclassName(ClassFile cf) {
662        try {
663            return getJavaName(cf.getSuperclassName());
664        } catch (ConstantPoolException e) {
665            return report(e);
666        }
667    }
668
669    String getJavaInterfaceName(ClassFile cf, int index) {
670        try {
671            return getJavaName(cf.getInterfaceName(index));
672        } catch (ConstantPoolException e) {
673            return report(e);
674        }
675    }
676
677    String getJavaFieldType(Descriptor d) {
678        try {
679            return getJavaName(d.getFieldType(constant_pool));
680        } catch (ConstantPoolException e) {
681            return report(e);
682        } catch (DescriptorException e) {
683            return report(e);
684        }
685    }
686
687    String getJavaReturnType(Descriptor d) {
688        try {
689            return getJavaName(d.getReturnType(constant_pool));
690        } catch (ConstantPoolException e) {
691            return report(e);
692        } catch (DescriptorException e) {
693            return report(e);
694        }
695    }
696
697    String getJavaParameterTypes(Descriptor d, AccessFlags flags) {
698        try {
699            return getJavaName(adjustVarargs(flags, d.getParameterTypes(constant_pool)));
700        } catch (ConstantPoolException e) {
701            return report(e);
702        } catch (DescriptorException e) {
703            return report(e);
704        }
705    }
706
707    String getJavaException(Exceptions_attribute attr, int index) {
708        try {
709            return getJavaName(attr.getException(index, constant_pool));
710        } catch (ConstantPoolException e) {
711            return report(e);
712        }
713    }
714
715    String getValue(Descriptor d) {
716        try {
717            return d.getValue(constant_pool);
718        } catch (ConstantPoolException e) {
719            return report(e);
720        }
721    }
722
723    String getFieldName(Field f) {
724        try {
725            return f.getName(constant_pool);
726        } catch (ConstantPoolException e) {
727            return report(e);
728        }
729    }
730
731    String getName(Method m) {
732        try {
733            return m.getName(constant_pool);
734        } catch (ConstantPoolException e) {
735            return report(e);
736        }
737    }
738
739    static String getJavaName(String name) {
740        return name.replace('/', '.');
741    }
742
743    String getSourceFile(SourceFile_attribute attr) {
744        try {
745            return attr.getSourceFile(constant_pool);
746        } catch (ConstantPoolException e) {
747            return report(e);
748        }
749    }
750
751    /**
752     * Get the value of an entry in the constant pool as a Java constant.
753     * Characters and booleans are represented by CONSTANT_Intgere entries.
754     * Character and string values are processed to escape characters outside
755     * the basic printable ASCII set.
756     * @param d the descriptor, giving the expected type of the constant
757     * @param index the index of the value in the constant pool
758     * @return a printable string containing the value of the constant.
759     */
760    String getConstantValue(Descriptor d, int index) {
761        try {
762            ConstantPool.CPInfo cpInfo = constant_pool.get(index);
763
764            switch (cpInfo.getTag()) {
765                case ConstantPool.CONSTANT_Integer: {
766                    ConstantPool.CONSTANT_Integer_info info =
767                            (ConstantPool.CONSTANT_Integer_info) cpInfo;
768                    String t = d.getValue(constant_pool);
769                    if (t.equals("C")) { // character
770                        return getConstantCharValue((char) info.value);
771                    } else if (t.equals("Z")) { // boolean
772                        return String.valueOf(info.value == 1);
773                    } else { // other: assume integer
774                        return String.valueOf(info.value);
775                    }
776                }
777
778                case ConstantPool.CONSTANT_String: {
779                    ConstantPool.CONSTANT_String_info info =
780                            (ConstantPool.CONSTANT_String_info) cpInfo;
781                    return getConstantStringValue(info.getString());
782                }
783
784                default:
785                    return constantWriter.stringValue(cpInfo);
786            }
787        } catch (ConstantPoolException e) {
788            return "#" + index;
789        }
790    }
791
792    private String getConstantCharValue(char c) {
793        StringBuilder sb = new StringBuilder();
794        sb.append('\'');
795        sb.append(esc(c, '\''));
796        sb.append('\'');
797        return sb.toString();
798    }
799
800    private String getConstantStringValue(String s) {
801        StringBuilder sb = new StringBuilder();
802        sb.append("\"");
803        for (int i = 0; i < s.length(); i++) {
804            sb.append(esc(s.charAt(i), '"'));
805        }
806        sb.append("\"");
807        return sb.toString();
808    }
809
810    private String esc(char c, char quote) {
811        if (32 <= c && c <= 126 && c != quote)
812            return String.valueOf(c);
813        else switch (c) {
814            case '\b': return "\\b";
815            case '\n': return "\\n";
816            case '\t': return "\\t";
817            case '\f': return "\\f";
818            case '\r': return "\\r";
819            case '\\': return "\\\\";
820            case '\'': return "\\'";
821            case '\"': return "\\\"";
822            default:   return String.format("\\u%04x", (int) c);
823        }
824    }
825
826    private Options options;
827    private AttributeWriter attrWriter;
828    private CodeWriter codeWriter;
829    private ConstantWriter constantWriter;
830    private ClassFile classFile;
831    private URI uri;
832    private long lastModified;
833    private String digestName;
834    private byte[] digest;
835    private int size;
836    private ConstantPool constant_pool;
837    private Method method;
838}
839