aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Readme.html9
-rw-r--r--src/main/javassist/ClassPool.java5
-rw-r--r--src/main/javassist/CtClass.java18
-rw-r--r--src/main/javassist/CtClassType.java503
-rw-r--r--src/main/javassist/CtConstructor.java1
-rw-r--r--src/main/javassist/CtField.java2
-rw-r--r--src/main/javassist/CtMember.java140
-rw-r--r--src/main/javassist/CtMethod.java9
-rw-r--r--src/main/javassist/CtNewClass.java1
-rw-r--r--src/main/javassist/CtNewWrappedMethod.java3
-rw-r--r--tutorial/tutorial.html58
11 files changed, 459 insertions, 290 deletions
diff --git a/Readme.html b/Readme.html
index a2847fe7..2a20f8ad 100644
--- a/Readme.html
+++ b/Readme.html
@@ -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>
diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java
index aff38282..d97d24d1 100644
--- a/src/main/javassist/ClassPool.java
+++ b/src/main/javassist/ClassPool.java
@@ -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.
diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java
index 62616c49..5fdd3495 100644
--- a/src/main/javassist/CtClass.java
+++ b/src/main/javassist/CtClass.java
@@ -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() {}
diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java
index 9e247061..1585423c 100644
--- a/src/main/javassist/CtClassType.java
+++ b/src/main/javassist/CtClassType.java
@@ -15,7 +15,10 @@
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();
diff --git a/src/main/javassist/CtConstructor.java b/src/main/javassist/CtConstructor.java
index 25d25275..54c89669 100644
--- a/src/main/javassist/CtConstructor.java
+++ b/src/main/javassist/CtConstructor.java
@@ -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;
}
/**
diff --git a/src/main/javassist/CtField.java b/src/main/javassist/CtField.java
index cbc50b8b..ce9c6ddd 100644
--- a/src/main/javassist/CtField.java
+++ b/src/main/javassist/CtField.java
@@ -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;
}
/**
diff --git a/src/main/javassist/CtMember.java b/src/main/javassist/CtMember.java
index 2f9d8ff0..2fde6cee 100644
--- a/src/main/javassist/CtMember.java
+++ b/src/main/javassist/CtMember.java
@@ -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("@");
diff --git a/src/main/javassist/CtMethod.java b/src/main/javassist/CtMethod.java
index 61c29713..158f21af 100644
--- a/src/main/javassist/CtMethod.java
+++ b/src/main/javassist/CtMethod.java
@@ -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() {
diff --git a/src/main/javassist/CtNewClass.java b/src/main/javassist/CtNewClass.java
index 3a2ee385..12b18465 100644
--- a/src/main/javassist/CtNewClass.java
+++ b/src/main/javassist/CtNewClass.java
@@ -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;
diff --git a/src/main/javassist/CtNewWrappedMethod.java b/src/main/javassist/CtNewWrappedMethod.java
index 5562e064..45916de9 100644
--- a/src/main/javassist/CtNewWrappedMethod.java
+++ b/src/main/javassist/CtNewWrappedMethod.java
@@ -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;
diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html
index 097d695a..b6876d4c 100644
--- a/tutorial/tutorial.html
+++ b/tutorial/tutorial.html
@@ -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