Home | History | Annotate | Download | only in javassist
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist;
     17 
     18 import java.lang.ref.WeakReference;
     19 import java.io.BufferedInputStream;
     20 import java.io.ByteArrayOutputStream;
     21 import java.io.ByteArrayInputStream;
     22 import java.io.DataInputStream;
     23 import java.io.DataOutputStream;
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.net.URL;
     27 import java.util.ArrayList;
     28 import java.util.HashMap;
     29 import java.util.Hashtable;
     30 import java.util.List;
     31 import java.util.Set;
     32 
     33 import javassist.bytecode.AccessFlag;
     34 import javassist.bytecode.AttributeInfo;
     35 import javassist.bytecode.AnnotationsAttribute;
     36 import javassist.bytecode.BadBytecode;
     37 import javassist.bytecode.Bytecode;
     38 import javassist.bytecode.ClassFile;
     39 import javassist.bytecode.CodeAttribute;
     40 import javassist.bytecode.ConstantAttribute;
     41 import javassist.bytecode.CodeIterator;
     42 import javassist.bytecode.ConstPool;
     43 import javassist.bytecode.Descriptor;
     44 import javassist.bytecode.EnclosingMethodAttribute;
     45 import javassist.bytecode.FieldInfo;
     46 import javassist.bytecode.InnerClassesAttribute;
     47 import javassist.bytecode.MethodInfo;
     48 import javassist.bytecode.ParameterAnnotationsAttribute;
     49 import javassist.bytecode.annotation.Annotation;
     50 import javassist.compiler.AccessorMaker;
     51 import javassist.compiler.CompileError;
     52 import javassist.compiler.Javac;
     53 import javassist.expr.ExprEditor;
     54 
     55 /**
     56  * Class types.
     57  */
     58 class CtClassType extends CtClass {
     59     ClassPool classPool;
     60     boolean wasChanged;
     61     private boolean wasFrozen;
     62     boolean wasPruned;
     63     boolean gcConstPool;    // if true, the constant pool entries will be garbage collected.
     64     ClassFile classfile;
     65     byte[] rawClassfile;    // backup storage
     66 
     67     private WeakReference memberCache;
     68     private AccessorMaker accessors;
     69 
     70     private FieldInitLink fieldInitializers;
     71     private Hashtable hiddenMethods;    // must be synchronous
     72     private int uniqueNumberSeed;
     73 
     74     private boolean doPruning = ClassPool.doPruning;
     75     private int getCount;
     76     private static final int GET_THRESHOLD = 2;     // see compress()
     77 
     78     CtClassType(String name, ClassPool cp) {
     79         super(name);
     80         classPool = cp;
     81         wasChanged = wasFrozen = wasPruned = gcConstPool = false;
     82         classfile = null;
     83         rawClassfile = null;
     84         memberCache = null;
     85         accessors = null;
     86         fieldInitializers = null;
     87         hiddenMethods = null;
     88         uniqueNumberSeed = 0;
     89         getCount = 0;
     90     }
     91 
     92     CtClassType(InputStream ins, ClassPool cp) throws IOException {
     93         this((String)null, cp);
     94         classfile = new ClassFile(new DataInputStream(ins));
     95         qualifiedName = classfile.getName();
     96     }
     97 
     98     protected void extendToString(StringBuffer buffer) {
     99         if (wasChanged)
    100             buffer.append("changed ");
    101 
    102         if (wasFrozen)
    103             buffer.append("frozen ");
    104 
    105         if (wasPruned)
    106             buffer.append("pruned ");
    107 
    108         buffer.append(Modifier.toString(getModifiers()));
    109         buffer.append(" class ");
    110         buffer.append(getName());
    111 
    112         try {
    113             CtClass ext = getSuperclass();
    114             if (ext != null) {
    115                 String name = ext.getName();
    116                 if (!name.equals("java.lang.Object"))
    117                     buffer.append(" extends " + ext.getName());
    118             }
    119         }
    120         catch (NotFoundException e) {
    121             buffer.append(" extends ??");
    122         }
    123 
    124         try {
    125             CtClass[] intf = getInterfaces();
    126             if (intf.length > 0)
    127                 buffer.append(" implements ");
    128 
    129             for (int i = 0; i < intf.length; ++i) {
    130                 buffer.append(intf[i].getName());
    131                 buffer.append(", ");
    132             }
    133         }
    134         catch (NotFoundException e) {
    135             buffer.append(" extends ??");
    136         }
    137 
    138         CtMember.Cache memCache = getMembers();
    139         exToString(buffer, " fields=",
    140                 memCache.fieldHead(), memCache.lastField());
    141         exToString(buffer, " constructors=",
    142                 memCache.consHead(), memCache.lastCons());
    143         exToString(buffer, " methods=",
    144                    memCache.methodHead(), memCache.lastMethod());
    145     }
    146 
    147     private void exToString(StringBuffer buffer, String msg,
    148                             CtMember head, CtMember tail) {
    149         buffer.append(msg);
    150         while (head != tail) {
    151             head = head.next();
    152             buffer.append(head);
    153             buffer.append(", ");
    154         }
    155     }
    156 
    157     public AccessorMaker getAccessorMaker() {
    158         if (accessors == null)
    159             accessors = new AccessorMaker(this);
    160 
    161         return accessors;
    162     }
    163 
    164     public ClassFile getClassFile2() {
    165         ClassFile cfile = classfile;
    166         if (cfile != null)
    167             return cfile;
    168 
    169         classPool.compress();
    170         if (rawClassfile != null) {
    171             try {
    172                 classfile = new ClassFile(new DataInputStream(
    173                                             new ByteArrayInputStream(rawClassfile)));
    174                 rawClassfile = null;
    175                 getCount = GET_THRESHOLD;
    176                 return classfile;
    177             }
    178             catch (IOException e) {
    179                 throw new RuntimeException(e.toString(), e);
    180             }
    181         }
    182 
    183         InputStream fin = null;
    184         try {
    185             fin = classPool.openClassfile(getName());
    186             if (fin == null)
    187                 throw new NotFoundException(getName());
    188 
    189             fin = new BufferedInputStream(fin);
    190             ClassFile cf = new ClassFile(new DataInputStream(fin));
    191             if (!cf.getName().equals(qualifiedName))
    192                 throw new RuntimeException("cannot find " + qualifiedName + ": "
    193                         + cf.getName() + " found in "
    194                         + qualifiedName.replace('.', '/') + ".class");
    195 
    196             classfile = cf;
    197             return cf;
    198         }
    199         catch (NotFoundException e) {
    200             throw new RuntimeException(e.toString(), e);
    201         }
    202         catch (IOException e) {
    203             throw new RuntimeException(e.toString(), e);
    204         }
    205         finally {
    206             if (fin != null)
    207                 try {
    208                     fin.close();
    209                 }
    210                 catch (IOException e) {}
    211         }
    212     }
    213 
    214    /* Inherited from CtClass.  Called by get() in ClassPool.
    215     *
    216     * @see javassist.CtClass#incGetCounter()
    217     * @see #toBytecode(DataOutputStream)
    218     */
    219    final void incGetCounter() { ++getCount; }
    220 
    221    /**
    222     * Invoked from ClassPool#compress().
    223     * It releases the class files that have not been recently used
    224     * if they are unmodified.
    225     */
    226    void compress() {
    227        if (getCount < GET_THRESHOLD)
    228            if (!isModified() && ClassPool.releaseUnmodifiedClassFile)
    229                removeClassFile();
    230            else if (isFrozen() && !wasPruned)
    231                saveClassFile();
    232 
    233        getCount = 0;
    234    }
    235 
    236    /**
    237      * Converts a ClassFile object into a byte array
    238      * for saving memory space.
    239      */
    240     private synchronized void saveClassFile() {
    241         /* getMembers() and releaseClassFile() are also synchronized.
    242          */
    243         if (classfile == null || hasMemberCache() != null)
    244             return;
    245 
    246         ByteArrayOutputStream barray = new ByteArrayOutputStream();
    247         DataOutputStream out = new DataOutputStream(barray);
    248         try {
    249             classfile.write(out);
    250             barray.close();
    251             rawClassfile = barray.toByteArray();
    252             classfile = null;
    253         }
    254         catch (IOException e) {}
    255     }
    256 
    257     private synchronized void removeClassFile() {
    258         if (classfile != null && !isModified() && hasMemberCache() == null)
    259             classfile = null;
    260     }
    261 
    262     public ClassPool getClassPool() { return classPool; }
    263 
    264     void setClassPool(ClassPool cp) { classPool = cp; }
    265 
    266     public URL getURL() throws NotFoundException {
    267         URL url = classPool.find(getName());
    268         if (url == null)
    269             throw new NotFoundException(getName());
    270         else
    271             return url;
    272     }
    273 
    274     public boolean isModified() { return wasChanged; }
    275 
    276     public boolean isFrozen() { return wasFrozen; }
    277 
    278     public void freeze() { wasFrozen = true; }
    279 
    280     void checkModify() throws RuntimeException {
    281         if (isFrozen()) {
    282             String msg = getName() + " class is frozen";
    283             if (wasPruned)
    284                 msg += " and pruned";
    285 
    286             throw new RuntimeException(msg);
    287         }
    288 
    289         wasChanged = true;
    290     }
    291 
    292     public void defrost() {
    293         checkPruned("defrost");
    294         wasFrozen = false;
    295     }
    296 
    297     public boolean subtypeOf(CtClass clazz) throws NotFoundException {
    298         int i;
    299         String cname = clazz.getName();
    300         if (this == clazz || getName().equals(cname))
    301             return true;
    302 
    303         ClassFile file = getClassFile2();
    304         String supername = file.getSuperclass();
    305         if (supername != null && supername.equals(cname))
    306             return true;
    307 
    308         String[] ifs = file.getInterfaces();
    309         int num = ifs.length;
    310         for (i = 0; i < num; ++i)
    311             if (ifs[i].equals(cname))
    312                 return true;
    313 
    314         if (supername != null && classPool.get(supername).subtypeOf(clazz))
    315             return true;
    316 
    317         for (i = 0; i < num; ++i)
    318             if (classPool.get(ifs[i]).subtypeOf(clazz))
    319                 return true;
    320 
    321         return false;
    322     }
    323 
    324     public void setName(String name) throws RuntimeException {
    325         String oldname = getName();
    326         if (name.equals(oldname))
    327             return;
    328 
    329         // check this in advance although classNameChanged() below does.
    330         classPool.checkNotFrozen(name);
    331         ClassFile cf = getClassFile2();
    332         super.setName(name);
    333         cf.setName(name);
    334         nameReplaced();
    335         classPool.classNameChanged(oldname, this);
    336     }
    337 
    338     public void replaceClassName(ClassMap classnames)
    339         throws RuntimeException
    340     {
    341         String oldClassName = getName();
    342         String newClassName
    343             = (String)classnames.get(Descriptor.toJvmName(oldClassName));
    344         if (newClassName != null) {
    345             newClassName = Descriptor.toJavaName(newClassName);
    346             // check this in advance although classNameChanged() below does.
    347             classPool.checkNotFrozen(newClassName);
    348         }
    349 
    350         super.replaceClassName(classnames);
    351         ClassFile cf = getClassFile2();
    352         cf.renameClass(classnames);
    353         nameReplaced();
    354 
    355         if (newClassName != null) {
    356             super.setName(newClassName);
    357             classPool.classNameChanged(oldClassName, this);
    358         }
    359     }
    360 
    361     public void replaceClassName(String oldname, String newname)
    362         throws RuntimeException
    363     {
    364         String thisname = getName();
    365         if (thisname.equals(oldname))
    366             setName(newname);
    367         else {
    368             super.replaceClassName(oldname, newname);
    369             getClassFile2().renameClass(oldname, newname);
    370             nameReplaced();
    371         }
    372     }
    373 
    374     public boolean isInterface() {
    375         return Modifier.isInterface(getModifiers());
    376     }
    377 
    378     public boolean isAnnotation() {
    379         return Modifier.isAnnotation(getModifiers());
    380     }
    381 
    382     public boolean isEnum() {
    383        return Modifier.isEnum(getModifiers());
    384     }
    385 
    386     public int getModifiers() {
    387         ClassFile cf = getClassFile2();
    388         int acc = cf.getAccessFlags();
    389         acc = AccessFlag.clear(acc, AccessFlag.SUPER);
    390         int inner = cf.getInnerAccessFlags();
    391         if (inner != -1 && (inner & AccessFlag.STATIC) != 0)
    392             acc |= AccessFlag.STATIC;
    393 
    394         return AccessFlag.toModifier(acc);
    395     }
    396 
    397     public CtClass[] getNestedClasses() throws NotFoundException {
    398         ClassFile cf = getClassFile2();
    399         InnerClassesAttribute ica
    400             = (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag);
    401         if (ica == null)
    402             return new CtClass[0];
    403 
    404         String thisName = cf.getName() + "$";
    405         int n = ica.tableLength();
    406         ArrayList list = new ArrayList(n);
    407         for (int i = 0; i < n; i++) {
    408             String name = ica.innerClass(i);
    409             if (name != null)
    410                 if (name.startsWith(thisName)) {
    411                     // if it is an immediate nested class
    412                     if (name.lastIndexOf('$') < thisName.length())
    413                         list.add(classPool.get(name));
    414                 }
    415         }
    416 
    417         return (CtClass[])list.toArray(new CtClass[list.size()]);
    418     }
    419 
    420     public void setModifiers(int mod) {
    421         ClassFile cf = getClassFile2();
    422         if (Modifier.isStatic(mod)) {
    423             int flags = cf.getInnerAccessFlags();
    424             if (flags != -1 && (flags & AccessFlag.STATIC) != 0)
    425                 mod = mod & ~Modifier.STATIC;
    426             else
    427                 throw new RuntimeException("cannot change " + getName() + " into a static class");
    428         }
    429 
    430         checkModify();
    431         cf.setAccessFlags(AccessFlag.of(mod));
    432     }
    433 
    434     public boolean hasAnnotation(Class clz) {
    435         ClassFile cf = getClassFile2();
    436         AnnotationsAttribute ainfo = (AnnotationsAttribute)
    437                 cf.getAttribute(AnnotationsAttribute.invisibleTag);
    438         AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
    439                 cf.getAttribute(AnnotationsAttribute.visibleTag);
    440         return hasAnnotationType(clz, getClassPool(), ainfo, ainfo2);
    441     }
    442 
    443     static boolean hasAnnotationType(Class clz, ClassPool cp,
    444                                      AnnotationsAttribute a1, AnnotationsAttribute a2)
    445     {
    446         Annotation[] anno1, anno2;
    447 
    448         if (a1 == null)
    449             anno1 = null;
    450         else
    451             anno1 = a1.getAnnotations();
    452 
    453         if (a2 == null)
    454             anno2 = null;
    455         else
    456             anno2 = a2.getAnnotations();
    457 
    458         String typeName = clz.getName();
    459         if (anno1 != null)
    460            for (int i = 0; i < anno1.length; i++)
    461               if (anno1[i].getTypeName().equals(typeName))
    462                   return true;
    463 
    464         if (anno2 != null)
    465            for (int i = 0; i < anno2.length; i++)
    466               if (anno2[i].getTypeName().equals(typeName))
    467                   return true;
    468 
    469         return false;
    470     }
    471 
    472     public Object getAnnotation(Class clz) throws ClassNotFoundException {
    473         ClassFile cf = getClassFile2();
    474         AnnotationsAttribute ainfo = (AnnotationsAttribute)
    475                 cf.getAttribute(AnnotationsAttribute.invisibleTag);
    476         AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
    477                 cf.getAttribute(AnnotationsAttribute.visibleTag);
    478         return getAnnotationType(clz, getClassPool(), ainfo, ainfo2);
    479     }
    480 
    481     static Object getAnnotationType(Class clz, ClassPool cp,
    482                                     AnnotationsAttribute a1, AnnotationsAttribute a2)
    483         throws ClassNotFoundException
    484     {
    485         Annotation[] anno1, anno2;
    486 
    487         if (a1 == null)
    488             anno1 = null;
    489         else
    490             anno1 = a1.getAnnotations();
    491 
    492         if (a2 == null)
    493             anno2 = null;
    494         else
    495             anno2 = a2.getAnnotations();
    496 
    497         String typeName = clz.getName();
    498         if (anno1 != null)
    499            for (int i = 0; i < anno1.length; i++)
    500               if (anno1[i].getTypeName().equals(typeName))
    501                   return toAnnoType(anno1[i], cp);
    502 
    503         if (anno2 != null)
    504            for (int i = 0; i < anno2.length; i++)
    505               if (anno2[i].getTypeName().equals(typeName))
    506                   return toAnnoType(anno2[i], cp);
    507 
    508         return null;
    509     }
    510 
    511     public Object[] getAnnotations() throws ClassNotFoundException {
    512        return getAnnotations(false);
    513     }
    514 
    515     public Object[] getAvailableAnnotations(){
    516        try {
    517            return getAnnotations(true);
    518        }
    519        catch (ClassNotFoundException e) {
    520            throw new RuntimeException("Unexpected exception ", e);
    521        }
    522     }
    523 
    524     private Object[] getAnnotations(boolean ignoreNotFound)
    525         throws ClassNotFoundException
    526     {
    527         ClassFile cf = getClassFile2();
    528         AnnotationsAttribute ainfo = (AnnotationsAttribute)
    529                 cf.getAttribute(AnnotationsAttribute.invisibleTag);
    530         AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
    531                 cf.getAttribute(AnnotationsAttribute.visibleTag);
    532         return toAnnotationType(ignoreNotFound, getClassPool(), ainfo, ainfo2);
    533     }
    534 
    535     static Object[] toAnnotationType(boolean ignoreNotFound, ClassPool cp,
    536                              AnnotationsAttribute a1, AnnotationsAttribute a2)
    537         throws ClassNotFoundException
    538     {
    539         Annotation[] anno1, anno2;
    540         int size1, size2;
    541 
    542         if (a1 == null) {
    543             anno1 = null;
    544             size1 = 0;
    545         }
    546         else {
    547             anno1 = a1.getAnnotations();
    548             size1 = anno1.length;
    549         }
    550 
    551         if (a2 == null) {
    552             anno2 = null;
    553             size2 = 0;
    554         }
    555         else {
    556             anno2 = a2.getAnnotations();
    557             size2 = anno2.length;
    558         }
    559 
    560         if (!ignoreNotFound){
    561            Object[] result = new Object[size1 + size2];
    562            for (int i = 0; i < size1; i++)
    563                result[i] = toAnnoType(anno1[i], cp);
    564 
    565            for (int j = 0; j < size2; j++)
    566                result[j + size1] = toAnnoType(anno2[j], cp);
    567 
    568            return result;
    569         }
    570         else{
    571            ArrayList annotations = new ArrayList();
    572            for (int i = 0 ; i < size1 ; i++){
    573               try{
    574                  annotations.add(toAnnoType(anno1[i], cp));
    575               }
    576               catch(ClassNotFoundException e){}
    577            }
    578            for (int j = 0; j < size2; j++) {
    579               try{
    580                  annotations.add(toAnnoType(anno2[j], cp));
    581               }
    582               catch(ClassNotFoundException e){}
    583            }
    584 
    585            return annotations.toArray();
    586         }
    587     }
    588 
    589     static Object[][] toAnnotationType(boolean ignoreNotFound, ClassPool cp,
    590                                        ParameterAnnotationsAttribute a1,
    591                                        ParameterAnnotationsAttribute a2,
    592                                        MethodInfo minfo)
    593         throws ClassNotFoundException
    594     {
    595         int numParameters = 0;
    596         if (a1 != null)
    597             numParameters = a1.numParameters();
    598         else if (a2 != null)
    599             numParameters = a2.numParameters();
    600         else
    601             numParameters = Descriptor.numOfParameters(minfo.getDescriptor());
    602 
    603         Object[][] result = new Object[numParameters][];
    604         for (int i = 0; i < numParameters; i++) {
    605             Annotation[] anno1, anno2;
    606             int size1, size2;
    607 
    608             if (a1 == null) {
    609                 anno1 = null;
    610                 size1 = 0;
    611             }
    612             else {
    613                 anno1 = a1.getAnnotations()[i];
    614                 size1 = anno1.length;
    615             }
    616 
    617             if (a2 == null) {
    618                 anno2 = null;
    619                 size2 = 0;
    620             }
    621             else {
    622                 anno2 = a2.getAnnotations()[i];
    623                 size2 = anno2.length;
    624             }
    625 
    626             if (!ignoreNotFound){
    627                 result[i] = new Object[size1 + size2];
    628                 for (int j = 0; j < size1; ++j)
    629                     result[i][j] = toAnnoType(anno1[j], cp);
    630 
    631                 for (int j = 0; j < size2; ++j)
    632                     result[i][j + size1] = toAnnoType(anno2[j], cp);
    633             }
    634             else{
    635                 ArrayList annotations = new ArrayList();
    636                 for (int j = 0 ; j < size1 ; j++){
    637                     try{
    638                         annotations.add(toAnnoType(anno1[j], cp));
    639                     }
    640                     catch(ClassNotFoundException e){}
    641                 }
    642                 for (int j = 0; j < size2; j++){
    643                     try{
    644                         annotations.add(toAnnoType(anno2[j], cp));
    645                     }
    646                     catch(ClassNotFoundException e){}
    647                 }
    648 
    649                 result[i] = annotations.toArray();
    650             }
    651         }
    652 
    653         return result;
    654     }
    655 
    656     private static Object toAnnoType(Annotation anno, ClassPool cp)
    657         throws ClassNotFoundException
    658     {
    659         try {
    660             ClassLoader cl = cp.getClassLoader();
    661             return anno.toAnnotationType(cl, cp);
    662         }
    663         catch (ClassNotFoundException e) {
    664             ClassLoader cl2 = cp.getClass().getClassLoader();
    665             return anno.toAnnotationType(cl2, cp);
    666         }
    667     }
    668 
    669     public boolean subclassOf(CtClass superclass) {
    670         if (superclass == null)
    671             return false;
    672 
    673         String superName = superclass.getName();
    674         CtClass curr = this;
    675         try {
    676             while (curr != null) {
    677                 if (curr.getName().equals(superName))
    678                     return true;
    679 
    680                 curr = curr.getSuperclass();
    681             }
    682         }
    683         catch (Exception ignored) {}
    684         return false;
    685     }
    686 
    687     public CtClass getSuperclass() throws NotFoundException {
    688         String supername = getClassFile2().getSuperclass();
    689         if (supername == null)
    690             return null;
    691         else
    692             return classPool.get(supername);
    693     }
    694 
    695     public void setSuperclass(CtClass clazz) throws CannotCompileException {
    696         checkModify();
    697         if (isInterface())
    698             addInterface(clazz);
    699         else
    700             getClassFile2().setSuperclass(clazz.getName());
    701     }
    702 
    703     public CtClass[] getInterfaces() throws NotFoundException {
    704         String[] ifs = getClassFile2().getInterfaces();
    705         int num = ifs.length;
    706         CtClass[] ifc = new CtClass[num];
    707         for (int i = 0; i < num; ++i)
    708             ifc[i] = classPool.get(ifs[i]);
    709 
    710         return ifc;
    711     }
    712 
    713     public void setInterfaces(CtClass[] list) {
    714         checkModify();
    715         String[] ifs;
    716         if (list == null)
    717             ifs = new String[0];
    718         else {
    719             int num = list.length;
    720             ifs = new String[num];
    721             for (int i = 0; i < num; ++i)
    722                 ifs[i] = list[i].getName();
    723         }
    724 
    725         getClassFile2().setInterfaces(ifs);
    726     }
    727 
    728     public void addInterface(CtClass anInterface) {
    729         checkModify();
    730         if (anInterface != null)
    731             getClassFile2().addInterface(anInterface.getName());
    732     }
    733 
    734     public CtClass getDeclaringClass() throws NotFoundException {
    735         ClassFile cf = getClassFile2();
    736         InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
    737                                                 InnerClassesAttribute.tag);
    738         if (ica == null)
    739             return null;
    740 
    741         String name = getName();
    742         int n = ica.tableLength();
    743         for (int i = 0; i < n; ++i)
    744             if (name.equals(ica.innerClass(i))) {
    745                 String outName = ica.outerClass(i);
    746                 if (outName != null)
    747                     return classPool.get(outName);
    748                 else {
    749                     // maybe anonymous or local class.
    750                     EnclosingMethodAttribute ema
    751                         = (EnclosingMethodAttribute)cf.getAttribute(
    752                                                     EnclosingMethodAttribute.tag);
    753                     if (ema != null)
    754                         return classPool.get(ema.className());
    755                 }
    756             }
    757 
    758         return null;
    759     }
    760 
    761     public CtMethod getEnclosingMethod() throws NotFoundException {
    762         ClassFile cf = getClassFile2();
    763         EnclosingMethodAttribute ema
    764                 = (EnclosingMethodAttribute)cf.getAttribute(
    765                                                 EnclosingMethodAttribute.tag);
    766         if (ema != null) {
    767             CtClass enc = classPool.get(ema.className());
    768             return enc.getMethod(ema.methodName(), ema.methodDescriptor());
    769         }
    770 
    771         return null;
    772     }
    773 
    774     public CtClass makeNestedClass(String name, boolean isStatic) {
    775         if (!isStatic)
    776             throw new RuntimeException(
    777                         "sorry, only nested static class is supported");
    778 
    779         checkModify();
    780         CtClass c = classPool.makeNestedClass(getName() + "$" + name);
    781         ClassFile cf = getClassFile2();
    782         ClassFile cf2 = c.getClassFile2();
    783         InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
    784                                                 InnerClassesAttribute.tag);
    785         if (ica == null) {
    786             ica = new InnerClassesAttribute(cf.getConstPool());
    787             cf.addAttribute(ica);
    788         }
    789 
    790         ica.append(c.getName(), this.getName(), name,
    791                    (cf2.getAccessFlags() & ~AccessFlag.SUPER) | AccessFlag.STATIC);
    792         cf2.addAttribute(ica.copy(cf2.getConstPool(), null));
    793         return c;
    794     }
    795 
    796     /* flush cached names.
    797      */
    798     private void nameReplaced() {
    799         CtMember.Cache cache = hasMemberCache();
    800         if (cache != null) {
    801             CtMember mth = cache.methodHead();
    802             CtMember tail = cache.lastMethod();
    803             while (mth != tail) {
    804                 mth = mth.next();
    805                 mth.nameReplaced();
    806             }
    807         }
    808     }
    809 
    810     /**
    811      * Returns null if members are not cached.
    812      */
    813     protected CtMember.Cache hasMemberCache() {
    814         if (memberCache != null)
    815             return (CtMember.Cache)memberCache.get();
    816         else
    817             return null;
    818     }
    819 
    820     protected synchronized CtMember.Cache getMembers() {
    821         CtMember.Cache cache = null;
    822         if (memberCache == null
    823             || (cache = (CtMember.Cache)memberCache.get()) == null) {
    824             cache = new CtMember.Cache(this);
    825             makeFieldCache(cache);
    826             makeBehaviorCache(cache);
    827             memberCache = new WeakReference(cache);
    828         }
    829 
    830         return cache;
    831     }
    832 
    833     private void makeFieldCache(CtMember.Cache cache) {
    834         List list = getClassFile2().getFields();
    835         int n = list.size();
    836         for (int i = 0; i < n; ++i) {
    837             FieldInfo finfo = (FieldInfo)list.get(i);
    838             CtField newField = new CtField(finfo, this);
    839             cache.addField(newField);
    840         }
    841     }
    842 
    843     private void makeBehaviorCache(CtMember.Cache cache) {
    844         List list = getClassFile2().getMethods();
    845         int n = list.size();
    846         for (int i = 0; i < n; ++i) {
    847             MethodInfo minfo = (MethodInfo)list.get(i);
    848             if (minfo.isMethod()) {
    849                 CtMethod newMethod = new CtMethod(minfo, this);
    850                 cache.addMethod(newMethod);
    851             }
    852             else {
    853                 CtConstructor newCons = new CtConstructor(minfo, this);
    854                 cache.addConstructor(newCons);
    855             }
    856         }
    857     }
    858 
    859     public CtField[] getFields() {
    860         ArrayList alist = new ArrayList();
    861         getFields(alist, this);
    862         return (CtField[])alist.toArray(new CtField[alist.size()]);
    863     }
    864 
    865     private static void getFields(ArrayList alist, CtClass cc) {
    866         int i, num;
    867         if (cc == null)
    868             return;
    869 
    870         try {
    871             getFields(alist, cc.getSuperclass());
    872         }
    873         catch (NotFoundException e) {}
    874 
    875         try {
    876             CtClass[] ifs = cc.getInterfaces();
    877             num = ifs.length;
    878             for (i = 0; i < num; ++i)
    879                 getFields(alist, ifs[i]);
    880         }
    881         catch (NotFoundException e) {}
    882 
    883         CtMember.Cache memCache = ((CtClassType)cc).getMembers();
    884         CtMember field = memCache.fieldHead();
    885         CtMember tail = memCache.lastField();
    886         while (field != tail) {
    887             field = field.next();
    888             if (!Modifier.isPrivate(field.getModifiers()))
    889                 alist.add(field);
    890         }
    891     }
    892 
    893     public CtField getField(String name, String desc) throws NotFoundException {
    894         CtField f = getField2(name, desc);
    895         return checkGetField(f, name, desc);
    896     }
    897 
    898     private CtField checkGetField(CtField f, String name, String desc)
    899         throws NotFoundException
    900     {
    901         if (f == null) {
    902             String msg = "field: " + name;
    903             if (desc != null)
    904                 msg += " type " + desc;
    905 
    906             throw new NotFoundException(msg + " in " + getName());
    907         }
    908         else
    909             return f;
    910     }
    911 
    912     CtField getField2(String name, String desc) {
    913         CtField df = getDeclaredField2(name, desc);
    914         if (df != null)
    915             return df;
    916 
    917         try {
    918             CtClass[] ifs = getInterfaces();
    919             int num = ifs.length;
    920             for (int i = 0; i < num; ++i) {
    921                 CtField f = ifs[i].getField2(name, desc);
    922                 if (f != null)
    923                     return f;
    924             }
    925 
    926             CtClass s = getSuperclass();
    927             if (s != null)
    928                 return s.getField2(name, desc);
    929         }
    930         catch (NotFoundException e) {}
    931         return null;
    932     }
    933 
    934     public CtField[] getDeclaredFields() {
    935         CtMember.Cache memCache = getMembers();
    936         CtMember field = memCache.fieldHead();
    937         CtMember tail = memCache.lastField();
    938         int num = CtMember.Cache.count(field, tail);
    939         CtField[] cfs = new CtField[num];
    940         int i = 0;
    941         while (field != tail) {
    942             field = field.next();
    943             cfs[i++] = (CtField)field;
    944         }
    945 
    946         return cfs;
    947     }
    948 
    949     public CtField getDeclaredField(String name) throws NotFoundException {
    950         return getDeclaredField(name, null);
    951     }
    952 
    953     public CtField getDeclaredField(String name, String desc) throws NotFoundException {
    954         CtField f = getDeclaredField2(name, desc);
    955         return checkGetField(f, name, desc);
    956     }
    957 
    958     private CtField getDeclaredField2(String name, String desc) {
    959         CtMember.Cache memCache = getMembers();
    960         CtMember field = memCache.fieldHead();
    961         CtMember tail = memCache.lastField();
    962         while (field != tail) {
    963             field = field.next();
    964             if (field.getName().equals(name)
    965                 && (desc == null || desc.equals(field.getSignature())))
    966                 return (CtField)field;
    967         }
    968 
    969         return null;
    970     }
    971 
    972     public CtBehavior[] getDeclaredBehaviors() {
    973         CtMember.Cache memCache = getMembers();
    974         CtMember cons = memCache.consHead();
    975         CtMember consTail = memCache.lastCons();
    976         int cnum = CtMember.Cache.count(cons, consTail);
    977         CtMember mth = memCache.methodHead();
    978         CtMember mthTail = memCache.lastMethod();
    979         int mnum = CtMember.Cache.count(mth, mthTail);
    980 
    981         CtBehavior[] cb = new CtBehavior[cnum + mnum];
    982         int i = 0;
    983         while (cons != consTail) {
    984             cons = cons.next();
    985             cb[i++] = (CtBehavior)cons;
    986         }
    987 
    988         while (mth != mthTail) {
    989             mth = mth.next();
    990             cb[i++] = (CtBehavior)mth;
    991         }
    992 
    993         return cb;
    994     }
    995 
    996     public CtConstructor[] getConstructors() {
    997         CtMember.Cache memCache = getMembers();
    998         CtMember cons = memCache.consHead();
    999         CtMember consTail = memCache.lastCons();
   1000 
   1001         int n = 0;
   1002         CtMember mem = cons;
   1003         while (mem != consTail) {
   1004             mem = mem.next();
   1005             if (isPubCons((CtConstructor)mem))
   1006                 n++;
   1007         }
   1008 
   1009         CtConstructor[] result = new CtConstructor[n];
   1010         int i = 0;
   1011         mem = cons;
   1012         while (mem != consTail) {
   1013             mem = mem.next();
   1014             CtConstructor cc = (CtConstructor)mem;
   1015             if (isPubCons(cc))
   1016                 result[i++] = cc;
   1017         }
   1018 
   1019         return result;
   1020     }
   1021 
   1022     private static boolean isPubCons(CtConstructor cons) {
   1023         return !Modifier.isPrivate(cons.getModifiers())
   1024                 && cons.isConstructor();
   1025     }
   1026 
   1027     public CtConstructor getConstructor(String desc)
   1028         throws NotFoundException
   1029     {
   1030         CtMember.Cache memCache = getMembers();
   1031         CtMember cons = memCache.consHead();
   1032         CtMember consTail = memCache.lastCons();
   1033 
   1034         while (cons != consTail) {
   1035             cons = cons.next();
   1036             CtConstructor cc = (CtConstructor)cons;
   1037             if (cc.getMethodInfo2().getDescriptor().equals(desc)
   1038                 && cc.isConstructor())
   1039                 return cc;
   1040         }
   1041 
   1042         return super.getConstructor(desc);
   1043     }
   1044 
   1045     public CtConstructor[] getDeclaredConstructors() {
   1046         CtMember.Cache memCache = getMembers();
   1047         CtMember cons = memCache.consHead();
   1048         CtMember consTail = memCache.lastCons();
   1049 
   1050         int n = 0;
   1051         CtMember mem = cons;
   1052         while (mem != consTail) {
   1053             mem = mem.next();
   1054             CtConstructor cc = (CtConstructor)mem;
   1055             if (cc.isConstructor())
   1056                 n++;
   1057         }
   1058 
   1059         CtConstructor[] result = new CtConstructor[n];
   1060         int i = 0;
   1061         mem = cons;
   1062         while (mem != consTail) {
   1063             mem = mem.next();
   1064             CtConstructor cc = (CtConstructor)mem;
   1065             if (cc.isConstructor())
   1066                 result[i++] = cc;
   1067         }
   1068 
   1069         return result;
   1070     }
   1071 
   1072     public CtConstructor getClassInitializer() {
   1073         CtMember.Cache memCache = getMembers();
   1074         CtMember cons = memCache.consHead();
   1075         CtMember consTail = memCache.lastCons();
   1076 
   1077         while (cons != consTail) {
   1078             cons = cons.next();
   1079             CtConstructor cc = (CtConstructor)cons;
   1080             if (cc.isClassInitializer())
   1081                 return cc;
   1082         }
   1083 
   1084         return null;
   1085     }
   1086 
   1087     public CtMethod[] getMethods() {
   1088         HashMap h = new HashMap();
   1089         getMethods0(h, this);
   1090         return (CtMethod[])h.values().toArray(new CtMethod[h.size()]);
   1091     }
   1092 
   1093     private static void getMethods0(HashMap h, CtClass cc) {
   1094         try {
   1095             CtClass[] ifs = cc.getInterfaces();
   1096             int size = ifs.length;
   1097             for (int i = 0; i < size; ++i)
   1098                 getMethods0(h, ifs[i]);
   1099         }
   1100         catch (NotFoundException e) {}
   1101 
   1102         try {
   1103             CtClass s = cc.getSuperclass();
   1104             if (s != null)
   1105                 getMethods0(h, s);
   1106         }
   1107         catch (NotFoundException e) {}
   1108 
   1109         if (cc instanceof CtClassType) {
   1110             CtMember.Cache memCache = ((CtClassType)cc).getMembers();
   1111             CtMember mth = memCache.methodHead();
   1112             CtMember mthTail = memCache.lastMethod();
   1113 
   1114             while (mth != mthTail) {
   1115                 mth = mth.next();
   1116                 if (!Modifier.isPrivate(mth.getModifiers()))
   1117                     h.put(((CtMethod)mth).getStringRep(), mth);
   1118             }
   1119         }
   1120     }
   1121 
   1122     public CtMethod getMethod(String name, String desc)
   1123         throws NotFoundException
   1124     {
   1125         CtMethod m = getMethod0(this, name, desc);
   1126         if (m != null)
   1127             return m;
   1128         else
   1129             throw new NotFoundException(name + "(..) is not found in "
   1130                                         + getName());
   1131     }
   1132 
   1133     private static CtMethod getMethod0(CtClass cc,
   1134                                        String name, String desc) {
   1135         if (cc instanceof CtClassType) {
   1136             CtMember.Cache memCache = ((CtClassType)cc).getMembers();
   1137             CtMember mth = memCache.methodHead();
   1138             CtMember mthTail = memCache.lastMethod();
   1139 
   1140             while (mth != mthTail) {
   1141                 mth = mth.next();
   1142                 if (mth.getName().equals(name)
   1143                         && ((CtMethod)mth).getMethodInfo2().getDescriptor().equals(desc))
   1144                     return (CtMethod)mth;
   1145             }
   1146         }
   1147 
   1148         try {
   1149             CtClass s = cc.getSuperclass();
   1150             if (s != null) {
   1151                 CtMethod m = getMethod0(s, name, desc);
   1152                 if (m != null)
   1153                     return m;
   1154             }
   1155         }
   1156         catch (NotFoundException e) {}
   1157 
   1158         try {
   1159             CtClass[] ifs = cc.getInterfaces();
   1160             int size = ifs.length;
   1161             for (int i = 0; i < size; ++i) {
   1162                 CtMethod m = getMethod0(ifs[i], name, desc);
   1163                 if (m != null)
   1164                     return m;
   1165             }
   1166         }
   1167         catch (NotFoundException e) {}
   1168         return null;
   1169     }
   1170 
   1171     public CtMethod[] getDeclaredMethods() {
   1172         CtMember.Cache memCache = getMembers();
   1173         CtMember mth = memCache.methodHead();
   1174         CtMember mthTail = memCache.lastMethod();
   1175         int num = CtMember.Cache.count(mth, mthTail);
   1176         CtMethod[] cms = new CtMethod[num];
   1177         int i = 0;
   1178         while (mth != mthTail) {
   1179             mth = mth.next();
   1180             cms[i++] = (CtMethod)mth;
   1181         }
   1182 
   1183         return cms;
   1184     }
   1185 
   1186     public CtMethod getDeclaredMethod(String name) throws NotFoundException {
   1187         CtMember.Cache memCache = getMembers();
   1188         CtMember mth = memCache.methodHead();
   1189         CtMember mthTail = memCache.lastMethod();
   1190         while (mth != mthTail) {
   1191             mth = mth.next();
   1192             if (mth.getName().equals(name))
   1193                 return (CtMethod)mth;
   1194         }
   1195 
   1196         throw new NotFoundException(name + "(..) is not found in "
   1197                                     + getName());
   1198     }
   1199 
   1200     public CtMethod getDeclaredMethod(String name, CtClass[] params)
   1201         throws NotFoundException
   1202     {
   1203         String desc = Descriptor.ofParameters(params);
   1204         CtMember.Cache memCache = getMembers();
   1205         CtMember mth = memCache.methodHead();
   1206         CtMember mthTail = memCache.lastMethod();
   1207 
   1208         while (mth != mthTail) {
   1209             mth = mth.next();
   1210             if (mth.getName().equals(name)
   1211                     && ((CtMethod)mth).getMethodInfo2().getDescriptor().startsWith(desc))
   1212                 return (CtMethod)mth;
   1213         }
   1214 
   1215         throw new NotFoundException(name + "(..) is not found in "
   1216                                     + getName());
   1217     }
   1218 
   1219     public void addField(CtField f, String init)
   1220         throws CannotCompileException
   1221     {
   1222         addField(f, CtField.Initializer.byExpr(init));
   1223     }
   1224 
   1225     public void addField(CtField f, CtField.Initializer init)
   1226         throws CannotCompileException
   1227     {
   1228         checkModify();
   1229         if (f.getDeclaringClass() != this)
   1230             throw new CannotCompileException("cannot add");
   1231 
   1232         if (init == null)
   1233             init = f.getInit();
   1234 
   1235         if (init != null) {
   1236             init.check(f.getSignature());
   1237             int mod = f.getModifiers();
   1238             if (Modifier.isStatic(mod) && Modifier.isFinal(mod))
   1239                 try {
   1240                     ConstPool cp = getClassFile2().getConstPool();
   1241                     int index = init.getConstantValue(cp, f.getType());
   1242                     if (index != 0) {
   1243                         f.getFieldInfo2().addAttribute(new ConstantAttribute(cp, index));
   1244                         init = null;
   1245                     }
   1246                 }
   1247                 catch (NotFoundException e) {}
   1248         }
   1249 
   1250         getMembers().addField(f);
   1251         getClassFile2().addField(f.getFieldInfo2());
   1252 
   1253         if (init != null) {
   1254             FieldInitLink fil = new FieldInitLink(f, init);
   1255             FieldInitLink link = fieldInitializers;
   1256             if (link == null)
   1257                 fieldInitializers = fil;
   1258             else {
   1259                 while (link.next != null)
   1260                     link = link.next;
   1261 
   1262                 link.next = fil;
   1263             }
   1264         }
   1265     }
   1266 
   1267     public void removeField(CtField f) throws NotFoundException {
   1268         checkModify();
   1269         FieldInfo fi = f.getFieldInfo2();
   1270         ClassFile cf = getClassFile2();
   1271         if (cf.getFields().remove(fi)) {
   1272             getMembers().remove(f);
   1273             gcConstPool = true;
   1274         }
   1275         else
   1276             throw new NotFoundException(f.toString());
   1277     }
   1278 
   1279     public CtConstructor makeClassInitializer()
   1280         throws CannotCompileException
   1281     {
   1282         CtConstructor clinit = getClassInitializer();
   1283         if (clinit != null)
   1284             return clinit;
   1285 
   1286         checkModify();
   1287         ClassFile cf = getClassFile2();
   1288         Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
   1289         modifyClassConstructor(cf, code, 0, 0);
   1290         return getClassInitializer();
   1291     }
   1292 
   1293     public void addConstructor(CtConstructor c)
   1294         throws CannotCompileException
   1295     {
   1296         checkModify();
   1297         if (c.getDeclaringClass() != this)
   1298             throw new CannotCompileException("cannot add");
   1299 
   1300         getMembers().addConstructor(c);
   1301         getClassFile2().addMethod(c.getMethodInfo2());
   1302     }
   1303 
   1304     public void removeConstructor(CtConstructor m) throws NotFoundException {
   1305         checkModify();
   1306         MethodInfo mi = m.getMethodInfo2();
   1307         ClassFile cf = getClassFile2();
   1308         if (cf.getMethods().remove(mi)) {
   1309             getMembers().remove(m);
   1310             gcConstPool = true;
   1311         }
   1312         else
   1313             throw new NotFoundException(m.toString());
   1314     }
   1315 
   1316     public void addMethod(CtMethod m) throws CannotCompileException {
   1317         checkModify();
   1318         if (m.getDeclaringClass() != this)
   1319             throw new CannotCompileException("bad declaring class");
   1320 
   1321         int mod = m.getModifiers();
   1322         if ((getModifiers() & Modifier.INTERFACE) != 0) {
   1323             m.setModifiers(mod | Modifier.PUBLIC);
   1324             if ((mod & Modifier.ABSTRACT) == 0)
   1325                 throw new CannotCompileException(
   1326                         "an interface method must be abstract: " + m.toString());
   1327         }
   1328 
   1329         getMembers().addMethod(m);
   1330         getClassFile2().addMethod(m.getMethodInfo2());
   1331         if ((mod & Modifier.ABSTRACT) != 0)
   1332             setModifiers(getModifiers() | Modifier.ABSTRACT);
   1333     }
   1334 
   1335     public void removeMethod(CtMethod m) throws NotFoundException {
   1336         checkModify();
   1337         MethodInfo mi = m.getMethodInfo2();
   1338         ClassFile cf = getClassFile2();
   1339         if (cf.getMethods().remove(mi)) {
   1340             getMembers().remove(m);
   1341             gcConstPool = true;
   1342         }
   1343         else
   1344             throw new NotFoundException(m.toString());
   1345     }
   1346 
   1347     public byte[] getAttribute(String name) {
   1348         AttributeInfo ai = getClassFile2().getAttribute(name);
   1349         if (ai == null)
   1350             return null;
   1351         else
   1352             return ai.get();
   1353     }
   1354 
   1355     public void setAttribute(String name, byte[] data) {
   1356         checkModify();
   1357         ClassFile cf = getClassFile2();
   1358         cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data));
   1359     }
   1360 
   1361     public void instrument(CodeConverter converter)
   1362         throws CannotCompileException
   1363     {
   1364         checkModify();
   1365         ClassFile cf = getClassFile2();
   1366         ConstPool cp = cf.getConstPool();
   1367         List list = cf.getMethods();
   1368         int n = list.size();
   1369         for (int i = 0; i < n; ++i) {
   1370             MethodInfo minfo = (MethodInfo)list.get(i);
   1371             converter.doit(this, minfo, cp);
   1372         }
   1373     }
   1374 
   1375     public void instrument(ExprEditor editor)
   1376         throws CannotCompileException
   1377     {
   1378         checkModify();
   1379         ClassFile cf = getClassFile2();
   1380         List list = cf.getMethods();
   1381         int n = list.size();
   1382         for (int i = 0; i < n; ++i) {
   1383             MethodInfo minfo = (MethodInfo)list.get(i);
   1384             editor.doit(this, minfo);
   1385         }
   1386     }
   1387 
   1388     /**
   1389      * @see javassist.CtClass#prune()
   1390      * @see javassist.CtClass#stopPruning(boolean)
   1391      */
   1392     public void prune() {
   1393         if (wasPruned)
   1394             return;
   1395 
   1396         wasPruned = wasFrozen = true;
   1397         getClassFile2().prune();
   1398     }
   1399 
   1400     public void rebuildClassFile() { gcConstPool = true; }
   1401 
   1402     public void toBytecode(DataOutputStream out)
   1403         throws CannotCompileException, IOException
   1404     {
   1405         try {
   1406             if (isModified()) {
   1407                 checkPruned("toBytecode");
   1408                 ClassFile cf = getClassFile2();
   1409                 if (gcConstPool) {
   1410                     cf.compact();
   1411                     gcConstPool = false;
   1412                 }
   1413 
   1414                 modifyClassConstructor(cf);
   1415                 modifyConstructors(cf);
   1416                 cf.write(out);
   1417                 out.flush();
   1418                 fieldInitializers = null;
   1419                 if (doPruning) {
   1420                     // to save memory
   1421                     cf.prune();
   1422                     wasPruned = true;
   1423                 }
   1424             }
   1425             else {
   1426                 classPool.writeClassfile(getName(), out);
   1427                 // to save memory
   1428                 // classfile = null;
   1429             }
   1430 
   1431             getCount = 0;
   1432             wasFrozen = true;
   1433         }
   1434         catch (NotFoundException e) {
   1435             throw new CannotCompileException(e);
   1436         }
   1437         catch (IOException e) {
   1438             throw new CannotCompileException(e);
   1439         }
   1440     }
   1441 
   1442     /* See also checkModified()
   1443      */
   1444     private void checkPruned(String method) {
   1445         if (wasPruned)
   1446             throw new RuntimeException(method + "(): " + getName()
   1447                                        + " was pruned.");
   1448     }
   1449 
   1450     public boolean stopPruning(boolean stop) {
   1451         boolean prev = !doPruning;
   1452         doPruning = !stop;
   1453         return prev;
   1454     }
   1455 
   1456     private void modifyClassConstructor(ClassFile cf)
   1457         throws CannotCompileException, NotFoundException
   1458     {
   1459         if (fieldInitializers == null)
   1460             return;
   1461 
   1462         Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
   1463         Javac jv = new Javac(code, this);
   1464         int stacksize = 0;
   1465         boolean doInit = false;
   1466         for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
   1467             CtField f = fi.field;
   1468             if (Modifier.isStatic(f.getModifiers())) {
   1469                 doInit = true;
   1470                 int s = fi.init.compileIfStatic(f.getType(), f.getName(),
   1471                                                 code, jv);
   1472                 if (stacksize < s)
   1473                     stacksize = s;
   1474             }
   1475         }
   1476 
   1477         if (doInit)    // need an initializer for static fileds.
   1478             modifyClassConstructor(cf, code, stacksize, 0);
   1479     }
   1480 
   1481     private void modifyClassConstructor(ClassFile cf, Bytecode code,
   1482                                         int stacksize, int localsize)
   1483         throws CannotCompileException
   1484     {
   1485         MethodInfo m = cf.getStaticInitializer();
   1486         if (m == null) {
   1487             code.add(Bytecode.RETURN);
   1488             code.setMaxStack(stacksize);
   1489             code.setMaxLocals(localsize);
   1490             m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V");
   1491             m.setAccessFlags(AccessFlag.STATIC);
   1492             m.setCodeAttribute(code.toCodeAttribute());
   1493             cf.addMethod(m);
   1494             CtMember.Cache cache = hasMemberCache();
   1495             if (cache != null)
   1496                 cache.addConstructor(new CtConstructor(m, this));
   1497         }
   1498         else {
   1499             CodeAttribute codeAttr = m.getCodeAttribute();
   1500             if (codeAttr == null)
   1501                 throw new CannotCompileException("empty <clinit>");
   1502 
   1503             try {
   1504                 CodeIterator it = codeAttr.iterator();
   1505                 int pos = it.insertEx(code.get());
   1506                 it.insert(code.getExceptionTable(), pos);
   1507                 int maxstack = codeAttr.getMaxStack();
   1508                 if (maxstack < stacksize)
   1509                     codeAttr.setMaxStack(stacksize);
   1510 
   1511                 int maxlocals = codeAttr.getMaxLocals();
   1512                 if (maxlocals < localsize)
   1513                     codeAttr.setMaxLocals(localsize);
   1514             }
   1515             catch (BadBytecode e) {
   1516                 throw new CannotCompileException(e);
   1517             }
   1518         }
   1519 
   1520         try {
   1521             m.rebuildStackMapIf6(classPool, cf);
   1522         }
   1523         catch (BadBytecode e) {
   1524             throw new CannotCompileException(e);
   1525         }
   1526     }
   1527 
   1528     private void modifyConstructors(ClassFile cf)
   1529         throws CannotCompileException, NotFoundException
   1530     {
   1531         if (fieldInitializers == null)
   1532             return;
   1533 
   1534         ConstPool cp = cf.getConstPool();
   1535         List list = cf.getMethods();
   1536         int n = list.size();
   1537         for (int i = 0; i < n; ++i) {
   1538             MethodInfo minfo = (MethodInfo)list.get(i);
   1539             if (minfo.isConstructor()) {
   1540                 CodeAttribute codeAttr = minfo.getCodeAttribute();
   1541                 if (codeAttr != null)
   1542                     try {
   1543                         Bytecode init = new Bytecode(cp, 0,
   1544                                                 codeAttr.getMaxLocals());
   1545                         CtClass[] params
   1546                             = Descriptor.getParameterTypes(
   1547                                                 minfo.getDescriptor(),
   1548                                                 classPool);
   1549                         int stacksize = makeFieldInitializer(init, params);
   1550                         insertAuxInitializer(codeAttr, init, stacksize);
   1551                         minfo.rebuildStackMapIf6(classPool, cf);
   1552                     }
   1553                     catch (BadBytecode e) {
   1554                         throw new CannotCompileException(e);
   1555                     }
   1556             }
   1557         }
   1558     }
   1559 
   1560     private static void insertAuxInitializer(CodeAttribute codeAttr,
   1561                                              Bytecode initializer,
   1562                                              int stacksize)
   1563         throws BadBytecode
   1564     {
   1565         CodeIterator it = codeAttr.iterator();
   1566         int index = it.skipSuperConstructor();
   1567         if (index < 0) {
   1568             index = it.skipThisConstructor();
   1569             if (index >= 0)
   1570                 return;         // this() is called.
   1571 
   1572             // Neither this() or super() is called.
   1573         }
   1574 
   1575         int pos = it.insertEx(initializer.get());
   1576         it.insert(initializer.getExceptionTable(), pos);
   1577         int maxstack = codeAttr.getMaxStack();
   1578         if (maxstack < stacksize)
   1579             codeAttr.setMaxStack(stacksize);
   1580     }
   1581 
   1582     private int makeFieldInitializer(Bytecode code, CtClass[] parameters)
   1583         throws CannotCompileException, NotFoundException
   1584     {
   1585         int stacksize = 0;
   1586         Javac jv = new Javac(code, this);
   1587         try {
   1588             jv.recordParams(parameters, false);
   1589         }
   1590         catch (CompileError e) {
   1591             throw new CannotCompileException(e);
   1592         }
   1593 
   1594         for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
   1595             CtField f = fi.field;
   1596             if (!Modifier.isStatic(f.getModifiers())) {
   1597                 int s = fi.init.compile(f.getType(), f.getName(), code,
   1598                                         parameters, jv);
   1599                 if (stacksize < s)
   1600                     stacksize = s;
   1601             }
   1602         }
   1603 
   1604         return stacksize;
   1605     }
   1606 
   1607     // Methods used by CtNewWrappedMethod
   1608 
   1609     Hashtable getHiddenMethods() {
   1610         if (hiddenMethods == null)
   1611             hiddenMethods = new Hashtable();
   1612 
   1613         return hiddenMethods;
   1614     }
   1615 
   1616     int getUniqueNumber() { return uniqueNumberSeed++; }
   1617 
   1618     public String makeUniqueName(String prefix) {
   1619         HashMap table = new HashMap();
   1620         makeMemberList(table);
   1621         Set keys = table.keySet();
   1622         String[] methods = new String[keys.size()];
   1623         keys.toArray(methods);
   1624 
   1625         if (notFindInArray(prefix, methods))
   1626             return prefix;
   1627 
   1628         int i = 100;
   1629         String name;
   1630         do {
   1631             if (i > 999)
   1632                 throw new RuntimeException("too many unique name");
   1633 
   1634             name = prefix + i++;
   1635         } while (!notFindInArray(name, methods));
   1636         return name;
   1637     }
   1638 
   1639     private static boolean notFindInArray(String prefix, String[] values) {
   1640         int len = values.length;
   1641         for (int i = 0; i < len; i++)
   1642             if (values[i].startsWith(prefix))
   1643                 return false;
   1644 
   1645         return true;
   1646     }
   1647 
   1648     private void makeMemberList(HashMap table) {
   1649         int mod = getModifiers();
   1650         if (Modifier.isAbstract(mod) || Modifier.isInterface(mod))
   1651             try {
   1652                 CtClass[] ifs = getInterfaces();
   1653                 int size = ifs.length;
   1654                 for (int i = 0; i < size; i++) {
   1655                     CtClass ic =ifs[i];
   1656                     if (ic != null && ic instanceof CtClassType)
   1657                         ((CtClassType)ic).makeMemberList(table);
   1658                 }
   1659             }
   1660             catch (NotFoundException e) {}
   1661 
   1662         try {
   1663             CtClass s = getSuperclass();
   1664             if (s != null && s instanceof CtClassType)
   1665                 ((CtClassType)s).makeMemberList(table);
   1666         }
   1667         catch (NotFoundException e) {}
   1668 
   1669         List list = getClassFile2().getMethods();
   1670         int n = list.size();
   1671         for (int i = 0; i < n; i++) {
   1672             MethodInfo minfo = (MethodInfo)list.get(i);
   1673             table.put(minfo.getName(), this);
   1674         }
   1675 
   1676         list = getClassFile2().getFields();
   1677         n = list.size();
   1678         for (int i = 0; i < n; i++) {
   1679             FieldInfo finfo = (FieldInfo)list.get(i);
   1680             table.put(finfo.getName(), this);
   1681         }
   1682     }
   1683 }
   1684 
   1685 class FieldInitLink {
   1686     FieldInitLink next;
   1687     CtField field;
   1688     CtField.Initializer init;
   1689 
   1690     FieldInitLink(CtField f, CtField.Initializer i) {
   1691         next = null;
   1692         field = f;
   1693         init = i;
   1694     }
   1695 }
   1696