* 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>
*
*/
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.
*
* @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) {
if (clazz == null)
throw new NotFoundException(classname);
- else
+ else {
+ clazz.incGetCounter();
return clazz;
+ }
}
/**
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();
/**
* 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,
}
/**
- * 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.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
+import java.util.Enumeration;
import java.util.List;
/**
class CtClassType extends CtClass {
ClassPool classPool;
boolean wasChanged;
- boolean wasFrozen;
+ private boolean wasFrozen;
+ boolean wasPruned;
ClassFile classfile;
private CtField fieldsCache;
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 {
if (wasFrozen)
buffer.append("frozen ");
+ if (wasPruned)
+ buffer.append("pruned ");
+
buffer.append(Modifier.toString(getModifiers()));
buffer.append(" class ");
buffer.append(getName());
if (classfile != null)
return classfile;
+ if (readCounter++ > READ_THRESHOLD) {
+ doCompaction();
+ readCounter = 0;
+ }
+
InputStream fin = null;
try {
fin = classPool.openClassfile(getName());
}
}
+ /* 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; }
wasChanged = true;
}
- public void defrost() { wasFrozen = false; }
+ public void defrost() {
+ checkPruned("defrost");
+ wasFrozen = false;
+ }
public boolean subtypeOf(CtClass clazz) throws NotFoundException {
int i;
{
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;
}
}
}
+ 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
{
if (!hasConstructor)
try {
inheritAllConstructors();
+ hasConstructor = true;
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
* @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 });
* this object unless the tree is copied back to this object by
* <code>setAnnotations()</code>.
*
- * @see #setAnnotations()
+ * @see #setAnnotations(Annotation[])
*/
public Annotation[] getAnnotations() {
try {
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.
*/
read(in);
}
+ void prune() {
+ classes = new HashMap();
+ strings = new HashMap();
+ }
+
/**
* Returns the name of the class using this constant pool table.
*/
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>.
read(src, methodname, classnameMap);
}
+ void prune(ConstPool cp) {
+ attribute = null;
+ name = cp.addUtf8Info(getName());
+ descriptor = cp.addUtf8Info(getDescriptor());
+ constPool = cp;
+ }
+
/**
* Returns a method name.
*/
* @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 {
/**
* 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:
* 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;
/**
* 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));
if (maker != null)
return maker.getConstructor(declClass, desc, minfo);
}
-
+
throw new CompileError("the called constructor is private in "
+ declClass.getName());
}
* 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