git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@125 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -37,6 +37,8 @@ import java.util.Hashtable; | |||
* of memory. To avoid this, a <code>ClassPool</code> object | |||
* should be recreated, for example, every hundred classes processed. | |||
* Note that <code>getDefault()</code> is a singleton factory. | |||
* Otherwise, <code>detach()</code> in <code>CtClass</code> should be used | |||
* to avoid huge memory consumption. | |||
* | |||
* <p><b><code>ClassPool</code> hierarchy:</b> | |||
* | |||
@@ -75,6 +77,8 @@ public class ClassPool { | |||
*/ | |||
private Hashtable cflow = null; // should be synchronous. | |||
private static final int INIT_HASH_SIZE = 191; | |||
/** | |||
* Creates a root class pool. No parent class pool is specified. | |||
* | |||
@@ -91,7 +95,7 @@ public class ClassPool { | |||
* @see javassist.ClassPool#getDefault() | |||
*/ | |||
public ClassPool(ClassPool parent) { | |||
this.classes = new Hashtable(); | |||
this.classes = new Hashtable(INIT_HASH_SIZE); | |||
this.source = new ClassPoolTail(); | |||
this.parent = parent; | |||
if (parent == null) { | |||
@@ -289,8 +293,10 @@ public class ClassPool { | |||
if (clazz == null) | |||
throw new NotFoundException(classname); | |||
else | |||
else { | |||
clazz.incGetCounter(); | |||
return clazz; | |||
} | |||
} | |||
/** |
@@ -355,7 +355,7 @@ public abstract class CtBehavior extends CtMember { | |||
throws CannotCompileException | |||
{ | |||
// if the class is not frozen, | |||
// does not trun the modified flag on. | |||
// does not turn the modified flag on. | |||
if (declaringClass.isFrozen()) | |||
declaringClass.checkModify(); | |||
@@ -241,17 +241,48 @@ public abstract class CtClass { | |||
/** | |||
* Defrosts the class so that the class can be modified again. | |||
* | |||
* To avoid changes that will be never reflected, | |||
* <p>To avoid changes that will be never reflected, | |||
* the class is frozen to be unmodifiable if it is loaded or | |||
* written out. This method should be called only in a case | |||
* that the class will be reloaded or written out later again. | |||
* | |||
* <p>If <code>defrost()</code> will be called later, pruning | |||
* must be disallowed in advance. | |||
* | |||
* @see #isFrozen() | |||
* @see #stopPruning(boolean) | |||
*/ | |||
public void defrost() { | |||
throw new RuntimeException("cannot defrost " + getName()); | |||
} | |||
/** | |||
* Disallows (or allows) pruning the data structure on memory | |||
* when this <code>CtClass</code> object is converted into a class file. | |||
* Pruning saves memory space since a <code>ClassPool</code> holds | |||
* all instances of <code>CtClass</code> | |||
* all the time of program execution. | |||
* However, pruning discards the data representing part of the | |||
* class definition, such as method bodies. | |||
* Therefore, once it is pruned, <code>toBytecode()</code>, | |||
* <code>writeFile()</code>, or <code>toClass()</code> cannot | |||
* be called again. | |||
* | |||
* <p>Initially, pruning is allowed. | |||
* | |||
* @param stop disallow pruning if true. Otherwise, allow. | |||
* @see #detach() | |||
* @see #toBytecode() | |||
* @see #toClass() | |||
* @see #writeFile() | |||
*/ | |||
public void stopPruning(boolean stop) {} | |||
/* Called by get() in ClassPool. | |||
* CtClassType overrides this method. | |||
*/ | |||
void incGetCounter() {} | |||
/** | |||
* Returns <code>true</code> if this object represents a primitive | |||
* Java type: boolean, byte, char, short, int, long, float, double, | |||
@@ -904,11 +935,13 @@ public abstract class CtClass { | |||
} | |||
/** | |||
* Removes this <code>CtClass</code> from the <code>ClassPool</code>. | |||
* Removes this <code>CtClass</code> object from the | |||
* <code>ClassPool</code>. | |||
* After this method is called, any method cannot be called on the | |||
* removed <code>CtClass</code> object. | |||
* | |||
* <p>If needed, | |||
* <p>If <code>get()</code> in <code>ClassPool</code> is called | |||
* with the name of the removed method, | |||
* the <code>ClassPool</code> will read the class file again | |||
* and constructs another <code>CtClass</code> object representing | |||
* the same class. |
@@ -29,6 +29,7 @@ import java.net.URL; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.Hashtable; | |||
import java.util.Enumeration; | |||
import java.util.List; | |||
/** | |||
@@ -37,7 +38,8 @@ import java.util.List; | |||
class CtClassType extends CtClass { | |||
ClassPool classPool; | |||
boolean wasChanged; | |||
boolean wasFrozen; | |||
private boolean wasFrozen; | |||
boolean wasPruned; | |||
ClassFile classfile; | |||
private CtField fieldsCache; | |||
@@ -51,16 +53,22 @@ class CtClassType extends CtClass { | |||
private Hashtable hiddenMethods; // must be synchronous | |||
private int uniqueNumberSeed; | |||
private boolean doPruning = true; | |||
int getCounter; | |||
private static int readCounter = 0; | |||
private static final int READ_THRESHOLD = 100; // see getClassFile2() | |||
CtClassType(String name, ClassPool cp) { | |||
super(name); | |||
classPool = cp; | |||
wasChanged = wasFrozen = false; | |||
wasChanged = wasFrozen = wasPruned = false; | |||
classfile = null; | |||
accessors = null; | |||
fieldInitializers = null; | |||
hiddenMethods = null; | |||
uniqueNumberSeed = 0; | |||
eraseCache(); | |||
getCounter = 0; | |||
} | |||
CtClassType(InputStream ins, ClassPool cp) throws IOException { | |||
@@ -76,6 +84,9 @@ class CtClassType extends CtClass { | |||
if (wasFrozen) | |||
buffer.append("frozen "); | |||
if (wasPruned) | |||
buffer.append("pruned "); | |||
buffer.append(Modifier.toString(getModifiers())); | |||
buffer.append(" class "); | |||
buffer.append(getName()); | |||
@@ -149,6 +160,11 @@ class CtClassType extends CtClass { | |||
if (classfile != null) | |||
return classfile; | |||
if (readCounter++ > READ_THRESHOLD) { | |||
doCompaction(); | |||
readCounter = 0; | |||
} | |||
InputStream fin = null; | |||
try { | |||
fin = classPool.openClassfile(getName()); | |||
@@ -174,6 +190,28 @@ class CtClassType extends CtClass { | |||
} | |||
} | |||
/* Inherited from CtClass. Called by get() in ClassPool. | |||
* | |||
* @see javassist.CtClass#incGetCounter() | |||
*/ | |||
void incGetCounter() { ++getCounter; } | |||
private void doCompaction() { | |||
Enumeration e = classPool.classes.elements(); | |||
while (e.hasMoreElements()) { | |||
Object obj = e.nextElement(); | |||
if (obj instanceof CtClassType) { | |||
CtClassType cct = (CtClassType)obj; | |||
if (cct.getCounter < 2 && !cct.isModified()) { | |||
cct.eraseCache(); | |||
cct.classfile = null; | |||
} | |||
cct.getCounter = 0; | |||
} | |||
} | |||
} | |||
public ClassPool getClassPool() { return classPool; } | |||
void setClassPool(ClassPool cp) { classPool = cp; } | |||
@@ -197,7 +235,10 @@ class CtClassType extends CtClass { | |||
wasChanged = true; | |||
} | |||
public void defrost() { wasFrozen = false; } | |||
public void defrost() { | |||
checkPruned("defrost"); | |||
wasFrozen = false; | |||
} | |||
public boolean subtypeOf(CtClass clazz) throws NotFoundException { | |||
int i; | |||
@@ -855,15 +896,25 @@ class CtClassType extends CtClass { | |||
{ | |||
try { | |||
if (isModified()) { | |||
checkPruned("toBytecode"); | |||
ClassFile cf = getClassFile2(); | |||
modifyClassConstructor(cf); | |||
modifyConstructors(cf); | |||
cf.write(out); | |||
out.flush(); | |||
fieldInitializers = null; | |||
if (doPruning) { | |||
// to save memory | |||
cf.prune(); | |||
wasPruned = true; | |||
} | |||
} | |||
else | |||
else { | |||
classPool.writeClassfile(getName(), out); | |||
// to save memory | |||
eraseCache(); | |||
classfile = null; | |||
} | |||
wasFrozen = true; | |||
} | |||
@@ -875,6 +926,16 @@ class CtClassType extends CtClass { | |||
} | |||
} | |||
private void checkPruned(String method) { | |||
if (wasPruned) | |||
throw new RuntimeException(method + "(): " + getName() | |||
+ " was pruned."); | |||
} | |||
public void stopPruning(boolean stop) { | |||
doPruning = !stop; | |||
} | |||
private void modifyClassConstructor(ClassFile cf) | |||
throws CannotCompileException, NotFoundException | |||
{ |
@@ -61,6 +61,7 @@ class CtNewClass extends CtClassType { | |||
if (!hasConstructor) | |||
try { | |||
inheritAllConstructors(); | |||
hasConstructor = true; | |||
} | |||
catch (NotFoundException e) { | |||
throw new CannotCompileException(e); |
@@ -84,7 +84,7 @@ public class AnnotationsAttribute extends AttributeInfo { | |||
* @param cp constant pool | |||
* @param attrname attribute name (<code>visibleTag</code> or | |||
* <code>invisibleTag</code>). | |||
* @see #setAnnotations(Annotations[]) | |||
* @see #setAnnotations(Annotation[]) | |||
*/ | |||
public AnnotationsAttribute(ConstPool cp, String attrname) { | |||
this(cp, attrname, new byte[] { 0, 0 }); | |||
@@ -127,7 +127,7 @@ public class AnnotationsAttribute extends AttributeInfo { | |||
* this object unless the tree is copied back to this object by | |||
* <code>setAnnotations()</code>. | |||
* | |||
* @see #setAnnotations() | |||
* @see #setAnnotations(Annotation[]) | |||
*/ | |||
public Annotation[] getAnnotations() { | |||
try { |
@@ -98,6 +98,42 @@ public final class ClassFile { | |||
return qname + ".java"; | |||
} | |||
/** | |||
* Discards all attributes, associated with both the class file and | |||
* the members such as a code attribute and exceptions attribute. | |||
* The unused constant pool entries are also discarded (a new packed | |||
* constant pool is constructed). | |||
*/ | |||
public void prune() { | |||
ConstPool cp = new ConstPool(thisclassname); | |||
superClass = cp.addClassInfo(getSuperclass()); | |||
if (interfaces != null) { | |||
int n = interfaces.length; | |||
for (int i = 0; i < n; ++i) | |||
interfaces[i] | |||
= cp.addClassInfo(constPool.getClassInfo(interfaces[i])); | |||
} | |||
ArrayList list = methods; | |||
int n = list.size(); | |||
for (int i = 0; i < n; ++i) { | |||
MethodInfo minfo = (MethodInfo)list.get(i); | |||
minfo.prune(cp); | |||
} | |||
list = fields; | |||
n = list.size(); | |||
for (int i = 0; i < n; ++i) { | |||
FieldInfo finfo = (FieldInfo)list.get(i); | |||
finfo.prune(cp); | |||
} | |||
attributes = new LinkedList(); | |||
cp.prune(); | |||
constPool = cp; | |||
} | |||
/** | |||
* Returns a constant pool table. | |||
*/ |
@@ -124,6 +124,11 @@ public final class ConstPool { | |||
read(in); | |||
} | |||
void prune() { | |||
classes = new HashMap(); | |||
strings = new HashMap(); | |||
} | |||
/** | |||
* Returns the name of the class using this constant pool table. | |||
*/ |
@@ -59,6 +59,13 @@ public final class FieldInfo { | |||
read(in); | |||
} | |||
void prune(ConstPool cp) { | |||
attribute = null; | |||
name = cp.addUtf8Info(getName()); | |||
descriptor = cp.addUtf8Info(getDescriptor()); | |||
constPool = cp; | |||
} | |||
/** | |||
* Returns the constant pool table used | |||
* by this <code>field_info</code>. |
@@ -95,6 +95,13 @@ public final class MethodInfo { | |||
read(src, methodname, classnameMap); | |||
} | |||
void prune(ConstPool cp) { | |||
attribute = null; | |||
name = cp.addUtf8Info(getName()); | |||
descriptor = cp.addUtf8Info(getDescriptor()); | |||
constPool = cp; | |||
} | |||
/** | |||
* Returns a method name. | |||
*/ |
@@ -123,7 +123,7 @@ public class ParameterAnnotationsAttribute extends AttributeInfo { | |||
* @return Each element of the returned array represents an array of | |||
* annotations that are associated with each method parameter. | |||
* | |||
* @see #setAnnotations() | |||
* @see #setAnnotations(Annotation[][]) | |||
*/ | |||
public Annotation[][] getAnnotations() { | |||
try { |
@@ -23,7 +23,6 @@ import javassist.bytecode.ConstPool; | |||
/** | |||
* A convenience class for constructing a | |||
* <code>..Annotations_attribute</code>. | |||
* It is typically used together with <code>AnnotationsVisitor</code>. | |||
* See the source code of the <code>AnnotationsAttribute.Copier</code> class. | |||
* | |||
* <p>The following code snippet is an example of use of this class: | |||
@@ -51,12 +50,11 @@ import javassist.bytecode.ConstPool; | |||
* corresponding to this annotation: | |||
* | |||
* <ul><pre> | |||
* @Author(name = "chiba", address = "tokyo") | |||
* @Author(name = "chiba", address = "tokyo") | |||
* </pre></ul> | |||
* | |||
* @see AnnotationsAttribute | |||
* @see ParameterAnnotationsAttribute | |||
* @see AnnotationsVisitor | |||
* @see javassist.bytecode.AnnotationsAttribute | |||
* @see javassist.bytecode.ParameterAnnotationsAttribute | |||
*/ | |||
public class AnnotationsWriter { | |||
private OutputStream output; |
@@ -64,7 +64,7 @@ public class EnumMemberValue extends MemberValue { | |||
/** | |||
* Changes the enum type name. | |||
* | |||
* @param classname a fully-qualified type name. | |||
* @param typename a fully-qualified type name. | |||
*/ | |||
public void setType(String typename) { | |||
typeIndex = cp.addUtf8Info(Descriptor.of(typename)); |
@@ -464,7 +464,7 @@ public class MemberCodeGen extends CodeGen { | |||
if (maker != null) | |||
return maker.getConstructor(declClass, desc, minfo); | |||
} | |||
throw new CompileError("the called constructor is private in " | |||
+ declClass.getName()); | |||
} |
@@ -332,14 +332,14 @@ public class ClassMetaobject implements Serializable { | |||
* to the original method in the reflected class (i.e. not the proxy | |||
* method), using the original name of the method. | |||
* | |||
* <p>Written by Brett Randall and Shigeru Chiba. | |||
* | |||
* @param originalName The original name of the reflected method | |||
* @param argTypes array of Class specifying the method signature | |||
* @return the identifier index of the original method | |||
* @throws NoSuchMethodException if the method does not exist | |||
* | |||
* @see ClassMetaobject#getMethod(int) | |||
* @author Brett Randall | |||
* @author Shigeru Chiba | |||
*/ | |||
public final int getMethodIndex(String originalName, Class[] argTypes) | |||
throws NoSuchMethodException |