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
16package javassist;
17
18import java.lang.ref.WeakReference;
19import java.io.BufferedInputStream;
20import java.io.ByteArrayOutputStream;
21import java.io.ByteArrayInputStream;
22import java.io.DataInputStream;
23import java.io.DataOutputStream;
24import java.io.IOException;
25import java.io.InputStream;
26import java.net.URL;
27import java.util.ArrayList;
28import java.util.HashMap;
29import java.util.Hashtable;
30import java.util.List;
31import java.util.Set;
32
33import javassist.bytecode.AccessFlag;
34import javassist.bytecode.AttributeInfo;
35import javassist.bytecode.AnnotationsAttribute;
36import javassist.bytecode.BadBytecode;
37import javassist.bytecode.Bytecode;
38import javassist.bytecode.ClassFile;
39import javassist.bytecode.CodeAttribute;
40import javassist.bytecode.ConstantAttribute;
41import javassist.bytecode.CodeIterator;
42import javassist.bytecode.ConstPool;
43import javassist.bytecode.Descriptor;
44import javassist.bytecode.EnclosingMethodAttribute;
45import javassist.bytecode.FieldInfo;
46import javassist.bytecode.InnerClassesAttribute;
47import javassist.bytecode.MethodInfo;
48import javassist.bytecode.ParameterAnnotationsAttribute;
49import javassist.bytecode.annotation.Annotation;
50import javassist.compiler.AccessorMaker;
51import javassist.compiler.CompileError;
52import javassist.compiler.Javac;
53import javassist.expr.ExprEditor;
54
55/**
56 * Class types.
57 */
58class 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
1685class 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