Browse Source

made automatic pruning off by default because I found that pruning does not really save memory (only 20%). I changed Javassist to compress a class file on memory after toBytecode().


git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@383 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
tags/rel_3_17_1_ga
chiba 17 years ago
parent
commit
73969fa11a

+ 8
- 1
Readme.html View 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>


+ 3
- 2
src/main/javassist/ClassPool.java View 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.

+ 13
- 5
src/main/javassist/CtClass.java View 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() {}


+ 293
- 210
src/main/javassist/CtClassType.java View File

@@ -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();

+ 0
- 1
src/main/javassist/CtConstructor.java View 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;
}

/**

+ 0
- 2
src/main/javassist/CtField.java View 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;
}

/**

+ 98
- 42
src/main/javassist/CtMember.java View 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("@");

+ 8
- 1
src/main/javassist/CtMethod.java View 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() {

+ 0
- 1
src/main/javassist/CtNewClass.java View 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;

+ 3
- 0
src/main/javassist/CtNewWrappedMethod.java View 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;

+ 33
- 25
tutorial/tutorial.html View 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

Loading…
Cancel
Save