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