]> source.dussan.org Git - javassist.git/commitdiff
made automatic pruning off by default because I found that pruning does not really...
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Fri, 8 Jun 2007 13:32:10 +0000 (13:32 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Fri, 8 Jun 2007 13:32:10 +0000 (13:32 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@383 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

Readme.html
src/main/javassist/ClassPool.java
src/main/javassist/CtClass.java
src/main/javassist/CtClassType.java
src/main/javassist/CtConstructor.java
src/main/javassist/CtField.java
src/main/javassist/CtMember.java
src/main/javassist/CtMethod.java
src/main/javassist/CtNewClass.java
src/main/javassist/CtNewWrappedMethod.java
tutorial/tutorial.html

index a2847fe79d8c16ec4ce6c66cfbeadb33e093fb55..2a20f8adbd2bac80e70f2a3932ba380ba0a3576d 100644 (file)
@@ -284,7 +284,14 @@ see javassist.Dump.
 <p>-version 3.6
 
 <ul>
-       <li>The stack map table introduced since Java 6 has been supported.
+    <li>The stack map table introduced since Java 6 has been supported.
+    <li>CtClass#getDeclaredBehaviors() now returns a class initializer
+        as well as methods and constructors.
+    <li>The default status of automatic pruning was made off.
+        Instead of pruning, this version of Javassist compresses
+        the data structure of a class file after toBytecode() is called.
+        The compressed class file is automatically decompressed when needed.
+        This saves memory space better than pruning.
     <li><a href="http://jira.jboss.com/jira/browse/JASSIST-33">JIRA JASSIST-33</a> has been fixed.
 </ul>
 
index aff38282bccf6f9a86e38a9ffc53a39f2ff60a3a..d97d24d1b4907d4f440bd0ede278bd60a6b82499 100644 (file)
@@ -111,12 +111,13 @@ public class ClassPool {
      * are called.  The automatic pruning can be turned on/off individually
      * for each <code>CtClass</code> object.
      *
-     * <p>The initial value is true.
+     * <p>The initial value is false.
      *
      * @see CtClass#prune()
      * @see CtClass#stopPruning(boolean)
+     * @see CtClass#detach()
      */
-    public static boolean doPruning = true;
+    public static boolean doPruning = false;
 
     /* releaseUnmodifiedClassFile was introduced for avoiding a bug
        of JBoss AOP.  So the value should be true except for JBoss AOP.
index 62616c49075169bf0c286704881dcc9ebcd80f18..5fdd3495ef127f944405ff481625322722a40acb 100644 (file)
@@ -52,7 +52,7 @@ public abstract class CtClass {
     /**
      * The version number of this release.
      */
-    public static final String version = "3.6.0BETA";
+    public static final String version = "3.6.0beta2";
 
     /**
      * Prints the version number and the copyright notice.
@@ -1126,7 +1126,8 @@ public abstract class CtClass {
      * save memory consumption.
      *
      * <p>If <code>ClassPool.doPruning</code> is true, the automatic pruning
-     * is on by default.  Otherwise, it is off.
+     * is on by default.  Otherwise, it is off.  The default value of
+     * <code>ClassPool.doPruning</code> is false.
      * 
      * @param stop      disallow pruning if true.  Otherwise, allow.
      * @return the previous status of pruning.  true if pruning is already stopped.
@@ -1150,15 +1151,22 @@ public abstract class CtClass {
      * are still accessible.
      *
      * <p><code>toBytecode()</code>, <code>writeFile()</code>, and
-     * <code>toClass()</code> internally call this method. 
+     * <code>toClass()</code> internally call this method if
+     * automatic pruning is on. 
+     *
+     * <p>According to some experiments, pruning does not really reduce
+     * memory consumption.  Only about 20%.  Since pruning takes time,
+     * it might not pay off.  So the automatic pruning is off by default.
+     *
+     * @see #stopPruning(boolean)
+     * @see #detach()
+     * @see ClassPool#doPruning
      *
      * @see #toBytecode()
      * @see #toClass()
      * @see #writeFile()
      * @see #instrument(CodeConverter)
      * @see #instrument(ExprEditor)
-     *
-     * @see #stopPruning(boolean)
      */
     public void prune() {}
 
index 9e247061be182106c94b4cb563878ffe13a09c47..1585423c7b4c0a8717be4755c9287f436b36c73d 100644 (file)
 
 package javassist;
 
+import java.lang.ref.WeakReference;
 import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -60,12 +63,9 @@ class CtClassType extends CtClass {
     boolean wasPruned;
     boolean memberRemoved;
     ClassFile classfile;
+    byte[] rawClassfile;    // backup storage
 
-    private CtMember fieldsCache;
-    private CtMember methodsCache;
-    private CtMember constructorsCache;
-    private CtConstructor classInitializerCache;
-
+    private WeakReference memberCache;
     private AccessorMaker accessors;
 
     private FieldInitLink fieldInitializers;
@@ -76,17 +76,19 @@ class CtClassType extends CtClass {
     int getCounter;
     private static int readCounter = 0;
     private static final int READ_THRESHOLD = 100;  // see getClassFile2()
+    private static final int GET_THRESHOLD = 2;     // see releaseClassFiles()
 
     CtClassType(String name, ClassPool cp) {
         super(name);
         classPool = cp;
         wasChanged = wasFrozen = wasPruned = memberRemoved = false;
         classfile = null;
+        rawClassfile = null;
+        memberCache = null;
         accessors = null;
         fieldInitializers = null;
         hiddenMethods = null;
         uniqueNumberSeed = 0;
-        eraseCache();
         getCounter = 0;
     }
 
@@ -136,38 +138,25 @@ class CtClassType extends CtClass {
             buffer.append(" extends ??");
         }
 
-        CtMember field = getFieldsCache();
-        buffer.append(" fields=");
-        while (field != null) {
-            buffer.append(field);
-            buffer.append(", ");
-            field = field.next;
-        }
-
-        CtMember c = getConstructorsCache();
-        buffer.append(" constructors=");
-        while (c != null) {
-            buffer.append(c);
-            buffer.append(", ");
-            c = c.next;
-        }
+        CtMember.Cache memCache = getMembers();
+        exToString(buffer, " fields=",
+                memCache.fieldHead(), memCache.lastField());
+        exToString(buffer, " constructors=",
+                memCache.consHead(), memCache.lastCons());
+        exToString(buffer, " methods=",
+                   memCache.methodHead(), memCache.lastMethod());
+    }
 
-        CtMember m = getMethodsCache();
-        buffer.append(" methods=");
-        while (m != null) {
-            buffer.append(m);
+    private void exToString(StringBuffer buffer, String msg,
+                            CtMember head, CtMember tail) {
+        buffer.append(msg);
+        while (head != tail) {
+            head = head.next();
+            buffer.append(head);
             buffer.append(", ");
-            m = m.next;
         }
     }
 
-    protected void eraseCache() {
-        fieldsCache = null;
-        constructorsCache = null;
-        classInitializerCache = null;
-        methodsCache = null;
-    }
-
     public AccessorMaker getAccessorMaker() {
         if (accessors == null)
             accessors = new AccessorMaker(this);
@@ -176,15 +165,29 @@ class CtClassType extends CtClass {
     }
 
     public ClassFile getClassFile2() {
-        if (classfile != null)
-            return classfile;
+        ClassFile cfile = classfile;
+        if (cfile != null)
+            return cfile;
 
-        if (readCounter++ > READ_THRESHOLD
-                                    && ClassPool.releaseUnmodifiedClassFile) {
+        if (readCounter++ > READ_THRESHOLD) {
+            getCounter += 2;
             releaseClassFiles();
             readCounter = 0;
         }
 
+        if (rawClassfile != null) {
+            try {
+                classfile = new ClassFile(new DataInputStream(
+                                            new ByteArrayInputStream(rawClassfile)));
+                rawClassfile = null;
+                getCounter = GET_THRESHOLD;
+                return classfile;
+            }
+            catch (IOException e) {
+                throw new RuntimeException(e.toString(), e);
+            }
+        }
+
         InputStream fin = null;
         try {
             fin = classPool.openClassfile(getName());
@@ -192,19 +195,20 @@ class CtClassType extends CtClass {
                 throw new NotFoundException(getName());
 
             fin = new BufferedInputStream(fin);
-            classfile = new ClassFile(new DataInputStream(fin));
-            if (!classfile.getName().equals(qualifiedName))
+            ClassFile cf = new ClassFile(new DataInputStream(fin));
+            if (!cf.getName().equals(qualifiedName))
                 throw new RuntimeException("cannot find " + qualifiedName + ": " 
-                        + classfile.getName() + " found in "
+                        + cf.getName() + " found in "
                         + qualifiedName.replace('.', '/') + ".class");
 
-            return classfile;
+            classfile = cf;
+            return cf;
         }
         catch (NotFoundException e) {
-            throw new RuntimeException(e.toString());
+            throw new RuntimeException(e.toString(), e);
         }
         catch (IOException e) {
-            throw new RuntimeException(e.toString());
+            throw new RuntimeException(e.toString(), e);
         }
         finally {
             if (fin != null)
@@ -215,14 +219,41 @@ class CtClassType extends CtClass {
         }
     }
 
+    /**
+     * Converts a ClassFile object into a byte array
+     * for saving memory space.
+     */
+    public synchronized void saveClassFile() {
+        /* getMembers() and releaseClassFile() are also synchronized.
+         */
+        if (classfile == null || hasMemberCache() != null)
+            return;
+
+        ByteArrayOutputStream barray = new ByteArrayOutputStream();
+        DataOutputStream out = new DataOutputStream(barray);
+        try {
+            classfile.write(out);
+            barray.close();
+            rawClassfile = barray.toByteArray();
+            classfile = null;
+        }
+        catch (IOException e) {}
+    }
+
+    public synchronized void releaseClassFile() {
+        if (!isModified())
+            classfile = null;
+    }
+
     /* Inherited from CtClass.  Called by get() in ClassPool.
      *
      * @see javassist.CtClass#incGetCounter()
+     * @see #toBytecode(DataOutputStream)
      */
     void incGetCounter() { ++getCounter; }
 
     /**
-     * Releases the class files and cached CtBehaviors
+     * Releases the class files
      * of the CtClasses that have not been recently used
      * if they are unmodified. 
      */
@@ -232,10 +263,11 @@ class CtClassType extends CtClass {
             Object obj = e.nextElement();
             if (obj instanceof CtClassType) {
                 CtClassType cct = (CtClassType)obj;
-                if (cct.getCounter < 2 && !cct.isModified()) {
-                    cct.eraseCache();
-                    cct.classfile = null;
-                }
+                if (cct.getCounter < GET_THRESHOLD)
+                    if (!cct.isModified() && ClassPool.releaseUnmodifiedClassFile)
+                        cct.releaseClassFile();
+                    else if (cct.isFrozen() && !cct.wasPruned)
+                        cct.saveClassFile();
 
                 cct.getCounter = 0;
             }
@@ -314,7 +346,7 @@ class CtClassType extends CtClass {
         ClassFile cf = getClassFile2();
         super.setName(name);
         cf.setName(name);
-        eraseCache();
+        nameReplaced();
         classPool.classNameChanged(oldname, this);
     }
 
@@ -333,7 +365,7 @@ class CtClassType extends CtClass {
         super.replaceClassName(classnames);
         ClassFile cf = getClassFile2();
         cf.renameClass(classnames);
-        eraseCache();
+        nameReplaced();
 
         if (newClassName != null) {
             super.setName(newClassName);
@@ -350,7 +382,7 @@ class CtClassType extends CtClass {
         else {
             super.replaceClassName(oldname, newname);
             getClassFile2().renameClass(oldname, newname);
-            eraseCache();
+            nameReplaced();
         }
     }
 
@@ -702,6 +734,69 @@ class CtClassType extends CtClass {
         return c;
     }
 
+    /* flush cached names.
+     */
+    private void nameReplaced() {
+        CtMember.Cache cache = hasMemberCache();
+        if (cache != null) {
+            CtMember mth = cache.methodHead();
+            CtMember tail = cache.lastMethod();
+            while (mth != tail) {
+                mth = mth.next();
+                mth.nameReplaced();
+            }
+        }
+    }
+
+    /**
+     * Returns null if members are not cached.
+     */
+    protected CtMember.Cache hasMemberCache() {
+        if (memberCache != null)
+            return (CtMember.Cache)memberCache.get();
+        else
+            return null;
+    }
+
+    protected synchronized CtMember.Cache getMembers() {
+        CtMember.Cache cache = null;
+        if (memberCache == null
+            || (cache = (CtMember.Cache)memberCache.get()) == null) {
+            cache = new CtMember.Cache(this);
+            makeFieldCache(cache);
+            makeBehaviorCache(cache);
+            memberCache = new WeakReference(cache);
+        }
+
+        return cache;
+    }
+
+    private void makeFieldCache(CtMember.Cache cache) {
+        List list = getClassFile2().getFields();
+        int n = list.size();
+        for (int i = 0; i < n; ++i) {
+            FieldInfo finfo = (FieldInfo)list.get(i);
+            CtField newField = new CtField(finfo, this);
+            cache.addField(newField);
+        }
+    }
+
+    private void makeBehaviorCache(CtMember.Cache cache) {
+        List list = getClassFile2().getMethods();
+        int n = list.size();
+        for (int i = 0; i < n; ++i) {
+            MethodInfo minfo = (MethodInfo)list.get(i);
+            if (minfo.isMethod()) {
+                CtMethod newMethod = new CtMethod(minfo, this);
+                cache.addMethod(newMethod);
+            }
+            else {
+                CtConstructor newCons = new CtConstructor(minfo, this);
+                cache.addConstructor(newCons);
+            }
+        }
+    }
+
     public CtField[] getFields() {
         ArrayList alist = new ArrayList();
         getFields(alist, this);
@@ -726,12 +821,13 @@ class CtClassType extends CtClass {
         }
         catch (NotFoundException e) {}
 
-        CtMember cf = ((CtClassType)cc).getFieldsCache();
-        while (cf != null) {
-            if (!Modifier.isPrivate(cf.getModifiers()))
-                alist.add(cf);
-
-            cf = cf.next;
+        CtMember.Cache memCache = ((CtClassType)cc).getMembers();
+        CtMember field = memCache.fieldHead();
+        CtMember tail = memCache.lastField();
+        while (field != tail) {
+            field = field.next();
+            if (!Modifier.isPrivate(field.getModifiers()))
+                alist.add(field);
         }
     }
 
@@ -766,37 +862,20 @@ class CtClassType extends CtClass {
     }
 
     public CtField[] getDeclaredFields() {
-        CtMember cf = getFieldsCache();
-        int num = CtField.count(cf);
+        CtMember.Cache memCache = getMembers();
+        CtMember field = memCache.fieldHead();
+        CtMember tail = memCache.lastField();
+        int num = CtMember.Cache.count(field, tail);
         CtField[] cfs = new CtField[num];
         int i = 0;
-        while (cf != null) {
-            cfs[i++] = (CtField)cf;
-            cf = cf.next;
+        while (field != tail) {
+            field = field.next();
+            cfs[i++] = (CtField)field;
         }
 
         return cfs;
     }
 
-    protected CtMember getFieldsCache() {
-        if (fieldsCache == null) {
-            List list = getClassFile2().getFields();
-            int n = list.size();
-            CtMember allFields = null;
-            CtField tail = null;
-            for (int i = 0; i < n; ++i) {
-                FieldInfo finfo = (FieldInfo)list.get(i);
-                CtField newTail = new CtField(finfo, this);
-                allFields = CtMember.append(allFields, tail, newTail);
-                tail = newTail;
-            }
-
-            fieldsCache = allFields;
-        }
-
-        return fieldsCache;
-    }
-
     public CtField getDeclaredField(String name) throws NotFoundException {
         CtField f = getDeclaredField2(name);
         if (f == null)
@@ -806,115 +885,131 @@ class CtClassType extends CtClass {
     }
 
     private CtField getDeclaredField2(String name) {
-        CtMember cf = getFieldsCache();
-        while (cf != null) {
-            if (cf.getName().equals(name))
-                return (CtField)cf;
-
-            cf = cf.next;
+        CtMember.Cache memCache = getMembers();
+        CtMember field = memCache.fieldHead();
+        CtMember tail = memCache.lastField();
+        while (field != tail) {
+            field = field.next();
+            if (field.getName().equals(name))
+                return (CtField)field;
         }
 
         return null;
     }
 
     public CtBehavior[] getDeclaredBehaviors() {
-        CtMember cc = getConstructorsCache();
-        CtMember cm = getMethodsCache();
-        int num = CtMember.count(cm) + CtMember.count(cc);
-        CtBehavior[] cb = new CtBehavior[num];
+        CtMember.Cache memCache = getMembers();
+        CtMember cons = memCache.consHead();
+        CtMember consTail = memCache.lastCons();
+        int cnum = CtMember.Cache.count(cons, consTail);
+        CtMember mth = memCache.methodHead();
+        CtMember mthTail = memCache.lastMethod();
+        int mnum = CtMember.Cache.count(mth, mthTail);
+
+        CtBehavior[] cb = new CtBehavior[cnum + mnum];
         int i = 0;
-        while (cc != null) {
-            cb[i++] = (CtBehavior)cc;
-            cc = cc.next;
+        while (cons != consTail) {
+            cons = cons.next();
+            cb[i++] = (CtBehavior)cons;
         }
 
-        while (cm != null) {
-            cb[i++] = (CtBehavior)cm;
-            cm = cm.next;
+        while (mth != mthTail) {
+            mth = mth.next();
+            cb[i++] = (CtBehavior)mth;
         }
 
         return cb;
     }
 
     public CtConstructor[] getConstructors() {
-        CtConstructor[] cons = getDeclaredConstructors();
-        if (cons.length == 0)
-            return cons;
+        CtMember.Cache memCache = getMembers();
+        CtMember cons = memCache.consHead();
+        CtMember consTail = memCache.lastCons();
 
         int n = 0;
-        int i = cons.length;
-        while (--i >= 0)
-            if (!Modifier.isPrivate(cons[i].getModifiers()))
-                ++n;
+        CtMember mem = cons;
+        while (mem != consTail) {
+            mem = mem.next();
+            if (isPubCons((CtConstructor)mem))
+                n++;
+        }
 
         CtConstructor[] result = new CtConstructor[n];
-        n = 0;
-        i = cons.length;
-        while (--i >= 0) {
-            CtConstructor c = cons[i];
-            if (!Modifier.isPrivate(c.getModifiers()))
-                result[n++] = c;
+        int i = 0;
+        mem = cons;
+        while (mem != consTail) {
+            mem = mem.next();
+            CtConstructor cc = (CtConstructor)mem;
+            if (isPubCons(cc))
+                result[i++] = cc;
         }
 
         return result;
     }
 
+    private static boolean isPubCons(CtConstructor cons) {
+        return !Modifier.isPrivate(cons.getModifiers())
+                && cons.isConstructor();
+    }
+
     public CtConstructor getConstructor(String desc)
         throws NotFoundException
     {
-        CtConstructor cc = (CtConstructor)getConstructorsCache();
-        while (cc != null) {
-            if (cc.getMethodInfo2().getDescriptor().equals(desc))
+        CtMember.Cache memCache = getMembers();
+        CtMember cons = memCache.consHead();
+        CtMember consTail = memCache.lastCons();
+
+        while (cons != consTail) {
+            cons = cons.next();
+            CtConstructor cc = (CtConstructor)cons;
+            if (cc.getMethodInfo2().getDescriptor().equals(desc)
+                && cc.isConstructor())
                 return cc;
-
-            cc = (CtConstructor)cc.next;
         }
 
         return super.getConstructor(desc);
     }
 
     public CtConstructor[] getDeclaredConstructors() {
-        CtMember cc = getConstructorsCache();
-        int num = CtMember.count(cc);
-        CtConstructor[] ccs = new CtConstructor[num];
-        int i = 0;
-        while (cc != null) {
-            ccs[i++] = (CtConstructor)cc;
-            cc = cc.next;
-        }
-
-        return ccs;
-    }
-
-    protected CtMember getConstructorsCache() {
-        if (constructorsCache == null) {
-            List list = getClassFile2().getMethods();
-            int n = list.size();
-            CtMember allConstructors = null;
-            CtConstructor tail = null;
-            for (int i = 0; i < n; ++i) {
-                MethodInfo minfo = (MethodInfo)list.get(i);
-                if (minfo.isConstructor()) {
-                    CtConstructor newTail = new CtConstructor(minfo, this);
-                    allConstructors = CtMember.append(allConstructors, tail, newTail);
-                    tail = newTail;
-                }
-            }
+        CtMember.Cache memCache = getMembers();
+        CtMember cons = memCache.consHead();
+        CtMember consTail = memCache.lastCons();
 
-            constructorsCache = allConstructors;
+        int n = 0;
+        CtMember mem = cons;
+        while (mem != consTail) {
+            mem = mem.next();
+            CtConstructor cc = (CtConstructor)mem;
+            if (cc.isConstructor())
+                n++;
         }
 
-        return constructorsCache;
+        CtConstructor[] result = new CtConstructor[n];
+        int i = 0;
+        mem = cons;
+        while (mem != consTail) {
+            mem = mem.next();
+            CtConstructor cc = (CtConstructor)mem;
+            if (cc.isConstructor())
+                result[i++] = cc;
+        }
+
+        return result;
     }
 
     public CtConstructor getClassInitializer() {
-        if (classInitializerCache == null) {
-            MethodInfo minfo = getClassFile2().getStaticInitializer();
-            if (minfo != null)
-                classInitializerCache = new CtConstructor(minfo, this);
+        CtMember.Cache memCache = getMembers();
+        CtMember cons = memCache.consHead();
+        CtMember consTail = memCache.lastCons();
+
+        while (cons != consTail) {
+            cons = cons.next();
+            CtConstructor cc = (CtConstructor)cons;
+            if (cc.isClassInitializer())
+                return cc;
         }
 
-        return classInitializerCache;
+        return null;
     }
 
     public CtMethod[] getMethods() {
@@ -940,12 +1035,14 @@ class CtClassType extends CtClass {
         catch (NotFoundException e) {}
 
         if (cc instanceof CtClassType) {
-            CtMember cm = ((CtClassType)cc).getMethodsCache();
-            while (cm != null) {
-                if (!Modifier.isPrivate(cm.getModifiers()))
-                    h.put(((CtMethod)cm).getStringRep(), cm);
-
-                cm = cm.next;
+            CtMember.Cache memCache = ((CtClassType)cc).getMembers();
+            CtMember mth = memCache.methodHead();
+            CtMember mthTail = memCache.lastMethod();
+
+            while (mth != mthTail) {
+                mth = mth.next();
+                if (!Modifier.isPrivate(mth.getModifiers()))
+                    h.put(((CtMethod)mth).getStringRep(), mth);
             }
         }
     }
@@ -964,13 +1061,15 @@ class CtClassType extends CtClass {
     private static CtMethod getMethod0(CtClass cc,
                                        String name, String desc) {
         if (cc instanceof CtClassType) {
-            CtMethod cm = (CtMethod)((CtClassType)cc).getMethodsCache();
-            while (cm != null) {
-                if (cm.getName().equals(name)
-                    && cm.getMethodInfo2().getDescriptor().equals(desc))
-                    return cm;
-
-                cm = (CtMethod)cm.next;
+            CtMember.Cache memCache = ((CtClassType)cc).getMembers();
+            CtMember mth = memCache.methodHead();
+            CtMember mthTail = memCache.lastMethod();
+
+            while (mth != mthTail) {
+                mth = mth.next();
+                if (mth.getName().equals(name)
+                        && ((CtMethod)mth).getMethodInfo2().getDescriptor().equals(desc))
+                    return (CtMethod)mth;
             }
         }
 
@@ -998,25 +1097,28 @@ class CtClassType extends CtClass {
     }
 
     public CtMethod[] getDeclaredMethods() {
-        CtMember cm = getMethodsCache();
-        int num = CtMember.count(cm);
+        CtMember.Cache memCache = getMembers();
+        CtMember mth = memCache.methodHead();
+        CtMember mthTail = memCache.lastMethod();
+        int num = CtMember.Cache.count(mth, mthTail);
         CtMethod[] cms = new CtMethod[num];
         int i = 0;
-        while (cm != null) {
-            cms[i++] = (CtMethod)cm;
-            cm = cm.next;
+        while (mth != mthTail) {
+            mth = mth.next();
+            cms[i++] = (CtMethod)mth;
         }
 
         return cms;
     }
 
     public CtMethod getDeclaredMethod(String name) throws NotFoundException {
-        CtMember m = getMethodsCache();
-        while (m != null) {
-            if (m.getName().equals(name))
-                return (CtMethod)m;
-
-            m = m.next;
+        CtMember.Cache memCache = getMembers();
+        CtMember mth = memCache.methodHead();
+        CtMember mthTail = memCache.lastMethod();
+        while (mth != mthTail) {
+            mth = mth.next();
+            if (mth.getName().equals(name))
+                return (CtMethod)mth;
         }
 
         throw new NotFoundException(name + "(..) is not found in "
@@ -1027,40 +1129,21 @@ class CtClassType extends CtClass {
         throws NotFoundException
     {
         String desc = Descriptor.ofParameters(params);
-        CtMethod m = (CtMethod)getMethodsCache();
-        while (m != null) {
-            if (m.getName().equals(name)
-                && m.getMethodInfo2().getDescriptor().startsWith(desc))
-                return m;
+        CtMember.Cache memCache = getMembers();
+        CtMember mth = memCache.methodHead();
+        CtMember mthTail = memCache.lastMethod();
 
-            m = (CtMethod)m.next;
+        while (mth != mthTail) {
+            mth = mth.next();
+            if (mth.getName().equals(name)
+                    && ((CtMethod)mth).getMethodInfo2().getDescriptor().startsWith(desc))
+                return (CtMethod)mth;
         }
 
         throw new NotFoundException(name + "(..) is not found in "
                                     + getName());
     }
 
-    protected CtMember getMethodsCache() {
-        if (methodsCache == null) {
-            List list = getClassFile2().getMethods();
-            int n = list.size();
-            CtMember allMethods = null;
-            CtMethod tail = null;
-            for (int i = 0; i < n; ++i) {
-                MethodInfo minfo = (MethodInfo)list.get(i);
-                if (minfo.isMethod()) {
-                    CtMethod newTail = new CtMethod(minfo, this);
-                    allMethods = CtMember.append(allMethods, tail, newTail);
-                    tail = newTail;
-                }
-            }
-
-            methodsCache = allMethods;
-        }
-
-        return methodsCache;
-    }
-
     public void addField(CtField f, String init)
         throws CannotCompileException
     {
@@ -1091,8 +1174,7 @@ class CtClassType extends CtClass {
                 catch (NotFoundException e) {}
         }
 
-        getFieldsCache();
-        fieldsCache = CtField.append(fieldsCache, f);
+        getMembers().addField(f);
         getClassFile2().addField(f.getFieldInfo2());
 
         if (init != null) {
@@ -1114,7 +1196,7 @@ class CtClassType extends CtClass {
         FieldInfo fi = f.getFieldInfo2();
         ClassFile cf = getClassFile2();
         if (cf.getFields().remove(fi)) {
-            fieldsCache = CtMember.remove(fieldsCache, f);
+            getMembers().remove(f);
             memberRemoved = true;
         }
         else
@@ -1142,8 +1224,7 @@ class CtClassType extends CtClass {
         if (c.getDeclaringClass() != this)
             throw new CannotCompileException("cannot add");
 
-        getConstructorsCache();
-        constructorsCache = (CtConstructor)CtMember.append(constructorsCache, c);
+        getMembers().addConstructor(c);
         getClassFile2().addMethod(c.getMethodInfo2());
     }
 
@@ -1152,7 +1233,7 @@ class CtClassType extends CtClass {
         MethodInfo mi = m.getMethodInfo2();
         ClassFile cf = getClassFile2();
         if (cf.getMethods().remove(mi)) {
-            constructorsCache = CtMember.remove(constructorsCache, m);
+            getMembers().remove(m);
             memberRemoved = true;
         }
         else
@@ -1164,8 +1245,7 @@ class CtClassType extends CtClass {
         if (m.getDeclaringClass() != this)
             throw new CannotCompileException("cannot add");
 
-        getMethodsCache();
-        methodsCache = CtMember.append(methodsCache, m);
+        getMembers().addMethod(m);
         getClassFile2().addMethod(m.getMethodInfo2());
         if ((m.getModifiers() & Modifier.ABSTRACT) != 0)
             setModifiers(getModifiers() | Modifier.ABSTRACT);
@@ -1176,7 +1256,7 @@ class CtClassType extends CtClass {
         MethodInfo mi = m.getMethodInfo2();
         ClassFile cf = getClassFile2();
         if (cf.getMethods().remove(mi)) {
-            methodsCache = CtMember.remove(methodsCache, m);
+            getMembers().remove(m);
             memberRemoved = true;
         }
         else
@@ -1262,10 +1342,10 @@ class CtClassType extends CtClass {
             else {
                 classPool.writeClassfile(getName(), out);
                 // to save memory
-                eraseCache();
-                classfile = null;
+                // classfile = null;
             }
 
+            getCounter = 0;
             wasFrozen = true;
         }
         catch (NotFoundException e) {
@@ -1328,6 +1408,9 @@ class CtClassType extends CtClass {
             m.setAccessFlags(AccessFlag.STATIC);
             m.setCodeAttribute(code.toCodeAttribute());
             cf.addMethod(m);
+            CtMember.Cache cache = hasMemberCache();
+            if (cache != null)
+                cache.addConstructor(new CtConstructor(m, this));
         }
         else {
             CodeAttribute codeAttr = m.getCodeAttribute();
index 25d252751e94af4d3b511d558b5394f4389c6e80..54c896693889432b4c22e1dc2b94b2da01c5584c 100644 (file)
@@ -35,7 +35,6 @@ import javassist.compiler.CompileError;
 public final class CtConstructor extends CtBehavior {
     protected CtConstructor(MethodInfo minfo, CtClass declaring) {
         super(declaring, minfo);
-        next = null;
     }
 
     /**
index cbc50b8b14fbf6ea3cda9bbdfb521a1d6c18ed89..ce9c6ddd5354bbd79ca7905ab2fed54de6f6edb0 100644 (file)
@@ -97,7 +97,6 @@ public class CtField extends CtMember {
         throws CannotCompileException
     {
         super(clazz);
-        next = null;
         ClassFile cf = clazz.getClassFile2();
         if (cf == null)
             throw new CannotCompileException("bad declaring class: "
@@ -109,7 +108,6 @@ public class CtField extends CtMember {
     CtField(FieldInfo fi, CtClass clazz) {
         super(clazz);
         fieldInfo = fi;
-        next = null;
     }
 
     /**
index 2f9d8ff0dc78855f0a3116e2542cdb51bac129fd..2fde6cee3079b517ea0d46268062cb9d1e3a090e 100644 (file)
@@ -20,64 +20,120 @@ package javassist;
  * or a method.
  */
 public abstract class CtMember {
-    protected CtMember next;          // for internal use
+    CtMember next;          // for internal use
     protected CtClass declaringClass;
 
-    protected CtMember(CtClass clazz) { declaringClass = clazz; }
+    /* Make a circular link of CtMembers declared in the
+     * same class so that they are garbage-collected together
+     * at the same time.
+     */
+    static class Cache extends CtMember {
+        protected void extendToString(StringBuffer buffer) {}
+        public Object[] getAnnotations()
+            throws ClassNotFoundException { return null; }
+        public byte[] getAttribute(String name) { return null; }
+        public Object[] getAvailableAnnotations()
+            throws ClassNotFoundException { return null; }
+        public int getModifiers() { return 0; }
+        public String getName() { return null; }
+        public String getSignature() { return null; }
+        public void setAttribute(String name, byte[] data) {}
+        public void setModifiers(int mod) {}
+
+        private CtMember methodTail;
+        private CtMember consTail;     // constructor tail
+        private CtMember fieldTail;
 
-    static CtMember append(CtMember list, CtMember previousTail, CtMember tail) {
-        tail.next = null;
-        if (list == null)
-            return tail;
-        else {
-            previousTail.next = tail;
-            return list;
+        Cache(CtClassType decl) {
+            super(decl);
+            methodTail = this;
+            consTail = this;
+            fieldTail = this;
+            fieldTail.next = this;
         }
-    }
 
-    static CtMember append(CtMember list, CtMember tail) {
-        tail.next = null;
-        if (list == null)
-            return tail;
-        else {
-            CtMember lst = list;
-            while (lst.next != null)
-                lst = lst.next;
-
-            lst.next = tail;
-            return list;
+        CtMember methodHead() { return this; }
+        CtMember lastMethod() { return methodTail; }
+        CtMember consHead() { return methodTail; }      // may include a static initializer
+        CtMember lastCons() { return consTail; }
+        CtMember fieldHead() { return consTail; }
+        CtMember lastField() { return fieldTail; }
+
+        void addMethod(CtMember method) {
+            method.next = methodTail.next;
+            methodTail.next = method;
+            if (methodTail == consTail) {
+                consTail = method;
+                if (methodTail == fieldTail)
+                    fieldTail = method;
+            }
+
+            methodTail = method;
         }
-    }
 
-    static int count(CtMember f) {
-        int n = 0;
-        while (f != null) {
-            ++n;
-            f = f.next;
+        /* Both constructors and a class initializer.
+         */
+        void addConstructor(CtMember cons) {
+            cons.next = consTail.next;
+            consTail.next = cons;
+            if (consTail == fieldTail)
+                fieldTail = cons;
+
+            consTail = cons;
         }
 
-        return n;
-    }
+        void addField(CtMember field) {
+            field.next = this; // or fieldTail.next
+            fieldTail.next = field;
+            fieldTail = field;
+        }
+
+        static int count(CtMember head, CtMember tail) {
+            int n = 0;
+            while (head != tail) {
+                n++;
+                head = head.next;
+            }
+
+            return n;
+        }
+
+        void remove(CtMember mem) {
+            CtMember m = this;
+            CtMember node;
+            while ((node = m.next) != this) {
+                if (node == mem) {
+                    m.next = node.next;
+                    if (node == methodTail)
+                        methodTail = m;
+                    else if (node == consTail)
+                        consTail = m;
+                    else if (node == fieldTail)
+                        fieldTail = m;
 
-    static CtMember remove(CtMember list, CtMember m) {
-        CtMember top = list;
-        if (list == null)
-            return null;
-        else if (list == m)
-            return list.next;
-        else
-            while (list.next != null) {
-                if (list.next == m) {
-                    list.next = list.next.next;
                     break;
                 }
-
-                list = list.next;
+                else
+                    m = m.next;
             }
+        }
+    }
 
-        return top;
+    protected CtMember(CtClass clazz) {
+        declaringClass = clazz;
+        next = null;
     }
 
+    final CtMember next() { return next; }
+
+    /**
+     * This method is invoked when setName() or replaceClassName()
+     * in CtClass is called.
+     *
+     * @see CtMethod#nameReplaced()
+     */
+    void nameReplaced() {}
+
     public String toString() {
         StringBuffer buffer = new StringBuffer(getClass().getName());
         buffer.append("@");
index 61c297130c75f3c79b19a29edc8726377fd321ed..158f21af96b471cd6b5335856980e956d75991e7 100644 (file)
@@ -32,7 +32,6 @@ public final class CtMethod extends CtBehavior {
 
     CtMethod(MethodInfo minfo, CtClass declaring) {
         super(declaring, minfo);
-        next = null;
         cachedStringRep = null;
     }
 
@@ -135,6 +134,14 @@ public final class CtMethod extends CtBehavior {
         return getStringRep().hashCode();
     }
 
+    /**
+     * This method is invoked when setName() or replaceClassName()
+     * in CtClass is called.
+     */
+    void nameReplaced() {
+        cachedStringRep = null;
+    }
+
     /* This method is also called by CtClassType.getMethods0(). 
      */
     final String getStringRep() {
index 3a2ee38516c0f2776853ddf0adc67578dc313781..12b18465a8405c489d7c3e59bf78096481ff08ff 100644 (file)
@@ -28,7 +28,6 @@ class CtNewClass extends CtClassType {
                boolean isInterface, CtClass superclass) {
         super(name, cp);
         wasChanged = true;
-        eraseCache();
         String superName;
         if (superclass == null)
             superName = null;
index 5562e064e114c23c6b01cae84cc47b4a069ddbba..45916de908d49e968875809b8d8176ad8c50a9b3 100644 (file)
@@ -152,6 +152,9 @@ class CtNewWrappedMethod {
             // a stack map is copied.  rebuilding it is not needed. 
             classfile.addMethod(body);
             bodies.put(src, bodyname);
+            CtMember.Cache cache = clazz.hasMemberCache();
+            if (cache != null)
+                cache.addMethod(new CtMethod(body, clazz));
         }
 
         return bodyname;
index 097d695a6e80cfbf2b555cbc0dfbfc40e7666b1b..b6876d4c6c5692b6eb2f65595c1429ddc2202abd 100644 (file)
@@ -140,19 +140,37 @@ not permitted.  This is for warning the developers when they attempt
 to modify a class file that has been already loaded since the JVM does
 not allow reloading a class.
 
-<p>When Javassist freezes a <code>CtClass</code> object, it also
-prunes the data structure contained in that object.  To reduce memory
-consumption, Javassist discards unnecessary attributes
+<p>A frozen <code>CtClass</code> can be defrost so that
+modifications of the class definition will be permitted.  For example,
+
+<ul><pre>
+CtClasss cc = ...;
+    :
+cc.writeFile();
+cc.defrost();
+cc.setSuperclass(...);    // OK since the class is not frozen.
+</pre></ul>
+
+<p>After <code>defrost()</code> is called, the <code>CtClass</code>
+object can be modified again.
+
+<p>If <code>ClassPool.doPruning</code> is set to <code>true</code>,
+then Javassist prunes the data structure contained
+in a <code>CtClass</code> object
+when Javassist freezes that object.
+To reduce memory
+consumption, pruning discards unnecessary attributes
 (<code>attribute_info</code> structures) in that object.
 For example, <code>Code_attribute</code> structures (method bodies)
 are discarded.
 Thus, after a
 <code>CtClass</code> object is pruned, the bytecode of a method is not
-accessible although method names, signatures, and annotations
-are still accessible.
+accessible except method names, signatures, and annotations.
+The pruned <code>CtClass</code> object cannot be defrost again.
+The default value of <code>ClassPool.doPruning</code> is <code>false</code>.
 
-<p>To disallow pruning a <code>CtClass</code>, <code>stopPruning()</code>
-must be called in advance:
+<p>To disallow pruning a particular <code>CtClass</code>,
+<code>stopPruning()</code> must be called on that object in advance:
 
 <ul><pre>
 CtClasss cc = ...;
@@ -162,29 +180,19 @@ cc.writeFile();                             // convert to a class file.
 // cc is not pruned.
 </pre></ul>
 
-<p>If a <code>CtClass</code> is not pruned, it can be defrost so that
-modifications of the class definition can be permitted.  For example,
-
-<ul><pre>
-CtClasss cc = ...;
-cc.stopPruning(true);
-    :
-cc.writeFile();
-cc.defrost();
-cc.setSuperclass(...);    // OK since the class is not frozen.
-</pre></ul>
-
-<p>To disallow pruning for all the <code>CtClass</code>es, set
-<code>ClassPool.doPruning</code> to <code>false</code>.
+<p>The <code>CtClass</code> object <code>cc</code> is not pruned.
+Thus it can be defrost after <code>writeFile()</code> is called.
 
 <ul><b>Note:</b>
-While debugging, you might want to temporarily stop pruning and write a modified
-class file to a disk drive.  <code>debugWriteFile()</code> is a convenient method
-for that purpose.  It stops pruning, write a class file, defrost it, and turn
-pruning on again (if it was initially on).
+While debugging, you might want to temporarily stop pruning and freezing
+and write a modified class file to a disk drive.
+<code>debugWriteFile()</code> is a convenient method
+for that purpose.  It stops pruning, writes a class file, defrosts it,
+and turns pruning on again (if it was initially on).
 </ul>
 
 
+
 <h4>Class search path</h4>
 
 <p>The default <code>ClassPool</code> returned