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