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