From f5d84c890b70def694dbd2904f1e6d2f7f1b70e3 Mon Sep 17 00:00:00 2001 From: bill Date: Thu, 9 Jun 2005 04:15:31 +0000 Subject: [PATCH] Fix pruning so that exceptions and annotation attributes are not ditched. Add more hooks for jBoss AOP so that it can prune aggressively. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@178 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/META-INF/MANIFEST.MF | 2 +- src/main/javassist/ClassPool.java | 18 +- src/main/javassist/CtClass.java | 16 +- src/main/javassist/CtClassType.java | 35 +- src/main/javassist/bytecode/ClassFile.java | 1356 ++++++++++--------- src/main/javassist/bytecode/FieldInfo.java | 27 +- src/main/javassist/bytecode/MethodInfo.java | 28 +- 7 files changed, 813 insertions(+), 669 deletions(-) diff --git a/src/main/META-INF/MANIFEST.MF b/src/main/META-INF/MANIFEST.MF index efb84210..06bf32f4 100644 --- a/src/main/META-INF/MANIFEST.MF +++ b/src/main/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.1 Specification-Title: Javassist Created-By: Shigeru Chiba, Tokyo Institute of Technology Specification-Vendor: Shigeru Chiba, Tokyo Institute of Technology -Specification-Version: 3.0 +Specification-Version: 3.1RC1 Main-Class: javassist.CtClass Name: javassist/ diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index bf1c73ac..05b0bb82 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -150,7 +150,7 @@ public class ClassPool { * Provide a hook so that subclasses can do their own * caching of classes. * - * @see #cacheCtClass(String,CtClass) + * @see #cacheCtClass(String,CtClass,boolean) * @see #removeCached(String) */ protected CtClass getCached(String classname) { @@ -164,7 +164,7 @@ public class ClassPool { * @see #getCached(String) * @see #removeCached(String,CtClass) */ - protected void cacheCtClass(String classname, CtClass c) { + protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { classes.put(classname, c); } @@ -173,7 +173,7 @@ public class ClassPool { * caching of classes. * * @see #getCached(String) - * @see #cacheCtClass(String,CtClass) + * @see #cacheCtClass(String,CtClass,boolean) */ protected CtClass removeCached(String classname) { return (CtClass)classes.remove(classname); @@ -272,7 +272,7 @@ public class ClassPool { String newName = clazz.getName(); checkNotFrozen(newName); - cacheCtClass(newName, clazz); + cacheCtClass(newName, clazz, false); } /** @@ -330,7 +330,7 @@ public class ClassPool { clazz = createCtClass(classname, useCache); if (clazz != null) { if (useCache) - cacheCtClass(classname, clazz); + cacheCtClass(classname, clazz, false); return clazz; } @@ -475,7 +475,7 @@ public class ClassPool { clazz.checkModify(); String classname = clazz.getName(); checkNotFrozen(classname); - cacheCtClass(classname, clazz); + cacheCtClass(classname, clazz, true); return clazz; } @@ -505,7 +505,7 @@ public class ClassPool { { checkNotFrozen(classname); CtClass clazz = new CtNewClass(classname, this, false, superclass); - cacheCtClass(classname, clazz); + cacheCtClass(classname, clazz, true); return clazz; } @@ -519,7 +519,7 @@ public class ClassPool { synchronized CtClass makeNestedClass(String classname) { checkNotFrozen(classname); CtClass clazz = new CtNewNestedClass(classname, this, false, null); - cacheCtClass(classname, clazz); + cacheCtClass(classname, clazz, true); return clazz; } @@ -549,7 +549,7 @@ public class ClassPool { { checkNotFrozen(name); CtClass clazz = new CtNewClass(name, this, true, superclass); - cacheCtClass(name, clazz); + cacheCtClass(name, clazz, true); return clazz; } diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 2acd5776..91123108 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -15,11 +15,6 @@ package javassist; -import javassist.bytecode.ClassFile; -import javassist.bytecode.Descriptor; -import javassist.bytecode.Opcode; -import javassist.expr.ExprEditor; - import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -29,6 +24,10 @@ import java.io.IOException; import java.io.OutputStream; import java.net.URL; import java.util.Collection; +import javassist.bytecode.ClassFile; +import javassist.bytecode.Descriptor; +import javassist.bytecode.Opcode; +import javassist.expr.ExprEditor; // Subclasses of CtClass: CtClassType, CtPrimitiveType, and CtArray @@ -1004,7 +1003,7 @@ public abstract class CtClass { ClassPool cp = getClassPool(); CtClass obj = cp.removeCached(getName()); if (obj != this) - cp.cacheCtClass(getName(), obj); + cp.cacheCtClass(getName(), obj, false); } /** @@ -1112,6 +1111,11 @@ public abstract class CtClass { } } + public void prune() + { + + } + /** * Converts this class to a class file. * Once this method is called, further modifications are not diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 4fd3ff90..17dfab27 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -15,6 +15,17 @@ package javassist; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; import javassist.bytecode.AccessFlag; import javassist.bytecode.AttributeInfo; import javassist.bytecode.BadBytecode; @@ -33,18 +44,6 @@ import javassist.compiler.CompileError; import javassist.compiler.Javac; import javassist.expr.ExprEditor; -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; - /** * Class types. */ @@ -174,10 +173,13 @@ class CtClassType extends CtClass { if (classfile != null) return classfile; + /* if (readCounter++ > READ_THRESHOLD) { + System.out.println("COMPACTING!!!!: " + getName()); doCompaction(); readCounter = 0; } + */ InputStream fin = null; try { @@ -962,6 +964,13 @@ class CtClassType extends CtClass { } } + public void prune() + { + if (wasPruned) return; + wasPruned = true; + getClassFile2().prune(); + } + public void toBytecode(DataOutputStream out) throws CannotCompileException, IOException { @@ -981,7 +990,7 @@ class CtClassType extends CtClass { fieldInitializers = null; if (doPruning) { // to save memory - cf.prune(); + cf. prune(); wasPruned = true; } } diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java index ddcd5d8d..77ba885b 100644 --- a/src/main/javassist/bytecode/ClassFile.java +++ b/src/main/javassist/bytecode/ClassFile.java @@ -18,12 +18,11 @@ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.Map; -import java.util.LinkedList; import java.util.ArrayList; -import java.util.ListIterator; +import java.util.LinkedList; import java.util.List; - +import java.util.ListIterator; +import java.util.Map; import javassist.CannotCompileException; /** @@ -32,631 +31,726 @@ import javassist.CannotCompileException; * * @see javassist.CtClass#getClassFile() */ -public final class ClassFile { - int major, minor; // version number - ConstPool constPool; - int thisClass; - int accessFlags; - int superClass; - int[] interfaces; - ArrayList fields; - ArrayList methods; - LinkedList attributes; - - String thisclassname; // not JVM-internal name - - /** - * Constructs a class file from a byte stream. - */ - public ClassFile(DataInputStream in) throws IOException { - read(in); - } - - /** - * Constructs a class file including no members. - * - * @param isInterface true if this is an interface. - * false if this is a class. - * @param classname a fully-qualified class name - * @param superclass a fully-qualified super class name - */ - public ClassFile(boolean isInterface, - String classname, String superclass) { - major = 45; - minor = 3; // JDK 1.1 or later - constPool = new ConstPool(classname); - thisClass = constPool.getThisClassInfo(); - if (isInterface) - accessFlags = AccessFlag.SUPER | AccessFlag.INTERFACE - | AccessFlag.ABSTRACT; - else - accessFlags = AccessFlag.SUPER; - - initSuperclass(superclass); - interfaces = null; - fields = new ArrayList(); - methods = new ArrayList(); - thisclassname = classname; - - attributes = new LinkedList(); - attributes.add(new SourceFileAttribute(constPool, - getSourcefileName(thisclassname))); - } - - private void initSuperclass(String superclass) { - if (superclass != null) - superClass = constPool.addClassInfo(superclass); - else - superClass = constPool.addClassInfo("java.lang.Object"); - } - - private static String getSourcefileName(String qname) { - int index = qname.lastIndexOf('.'); - if (index >= 0) - qname = qname.substring(index + 1); - - return qname + ".java"; - } - - /** - * Eliminates dead constant pool items. If a method or a field is removed, - * the constant pool items used by that method/field become dead items. - * This method recreates a constant pool. - */ - public void compact() { - ConstPool cp = compact0(); - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - minfo.compact(cp); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - finfo.compact(cp); - } - - attributes = AttributeInfo.copyAll(attributes, cp); - constPool = cp; - } - - private ConstPool compact0() { - ConstPool cp = new ConstPool(thisclassname); - thisClass = cp.getThisClassInfo(); - 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])); - } - - return cp; - } - - /** - * 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 = compact0(); - - 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. - */ - public ConstPool getConstPool() { - return constPool; - } - - /** - * Returns true if this is an interface. - */ - public boolean isInterface() { - return (accessFlags & AccessFlag.INTERFACE) != 0; - } - - /** - * Returns true if this is a final class or interface. - */ - public boolean isFinal() { - return (accessFlags & AccessFlag.FINAL) != 0; - } - - /** - * Returns true if this is an abstract class or an interface. - */ - public boolean isAbstract() { - return (accessFlags & AccessFlag.ABSTRACT) != 0; - } - - /** - * Returns access flags. - * - * @see javassist.bytecode.AccessFlag - */ - public int getAccessFlags() { - return accessFlags; - } - - /** - * Changes access flags. - * - * @see javassist.bytecode.AccessFlag - */ - public void setAccessFlags(int acc) { - accessFlags = acc | AccessFlag.SUPER; - } - - /** - * Returns the class name. - */ - public String getName() { - return thisclassname; - } - - /** - * Sets the class name. This method substitutes the new name - * for all occurrences of the old class name in the class file. - */ - public void setName(String name) { - renameClass(thisclassname, name); - } - - /** - * Returns the super class name. - */ - public String getSuperclass() { - return constPool.getClassInfo(superClass); - } - - /** - * Returns the index of the constant pool entry representing - * the super class. - */ - public int getSuperclassId() { - return superClass; - } - - /** - * Sets the super class. - * - *

This method modifies constructors so that they call - * constructors declared in the new super class. - */ - public void setSuperclass(String superclass) - throws CannotCompileException - { - if (superclass == null) - superclass = "java.lang.Object"; - - try { - superClass = constPool.addClassInfo(superclass); - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - minfo.setSuperclass(superclass); - } - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } - } - - /** - * Replaces all occurrences of a class name in the class file. - * - *

If class X is substituted for class Y in the class file, - * X and Y must have the same signature. If Y provides a method - * m(), X must provide it even if X inherits m() from the super class. - * If this fact is not guaranteed, the bytecode verifier may cause - * an error. - * - * @param oldname the replaced class name - * @param newname the substituted class name - */ - public final void renameClass(String oldname, String newname) { - ArrayList list; - int n; - - if (oldname.equals(newname)) - return; - - if (oldname.equals(thisclassname)) - thisclassname = newname; - - oldname = Descriptor.toJvmName(oldname); - newname = Descriptor.toJvmName(newname); - constPool.renameClass(oldname, newname); - - list = methods; - n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - String desc = minfo.getDescriptor(); - minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - String desc = finfo.getDescriptor(); - finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); - } - } - - /** - * Replaces all occurrences of several class names in the class file. - * - * @param classnames specifies which class name is replaced - * with which new name. Class names must - * be described with the JVM-internal - * representation like - * java/lang/Object. - * - * @see #renameClass(String,String) - */ - public final void renameClass(Map classnames) { - String jvmNewThisName - = (String)classnames.get(Descriptor.toJvmName(thisclassname)); - if (jvmNewThisName != null) - thisclassname = Descriptor.toJavaName(jvmNewThisName); - - constPool.renameClass(classnames); - - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - String desc = minfo.getDescriptor(); - minfo.setDescriptor(Descriptor.rename(desc, classnames)); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - String desc = finfo.getDescriptor(); - finfo.setDescriptor(Descriptor.rename(desc, classnames)); - } - } - - /** - * Returns the names of the interfaces implemented by the class. - */ - public String[] getInterfaces() { - if (interfaces == null) - return new String[0]; - else { - int n = interfaces.length; - String[] list = new String[n]; - for (int i = 0; i < n; ++i) - list[i] = constPool.getClassInfo(interfaces[i]); - - return list; - } - } - - /** - * Sets the interfaces. - * - * @param nameList the names of the interfaces. - */ - public void setInterfaces(String[] nameList) { - if (nameList != null) { - int n = nameList.length; - interfaces = new int[n]; - for (int i = 0; i < n; ++i) - interfaces[i] = constPool.addClassInfo(nameList[i]); - } - } - - /** - * Appends an interface to the - * interfaces implemented by the class. - */ - public void addInterface(String name) { - int info = constPool.addClassInfo(name); - if (interfaces == null) { - interfaces = new int[1]; - interfaces[0] = info; - } - else { - int n = interfaces.length; - int[] newarray = new int[n + 1]; - System.arraycopy(interfaces, 0, newarray, 0, n); - newarray[n] = info; - interfaces = newarray; - } - } - - /** - * Returns all the fields declared in the class. - * - * @return a list of FieldInfo. - * @see FieldInfo - */ - public List getFields() { - return fields; - } - - /** - * Appends a field to the class. - */ - public void addField(FieldInfo finfo) throws CannotCompileException { - testExistingField(finfo.getName(), finfo.getDescriptor()); - fields.add(finfo); - } - - private void addField0(FieldInfo finfo) { - fields.add(finfo); - } - - private void testExistingField(String name, String descriptor) - throws CannotCompileException - { - ListIterator it = fields.listIterator(0); - while (it.hasNext()) { - FieldInfo minfo = (FieldInfo)it.next(); - if (minfo.getName().equals(name)) - throw new CannotCompileException("duplicate field: " + name); - } - } - - /** - * Returns all the methods declared in the class. - * - * @return a list of MethodInfo. - * @see MethodInfo - */ - public List getMethods() { - return methods; - } - - /** - * Returns the method with the specified name. If there are multiple - * methods with that name, this method returns one of them. - * - * @return null if no such a method is found. - */ - public MethodInfo getMethod(String name) { - ArrayList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - if (minfo.getName().equals(name)) - return minfo; - } - - return null; - } - - /** - * Returns a static initializer (class initializer), or null if - * it does not exist. - */ - public MethodInfo getStaticInitializer() { - return getMethod(MethodInfo.nameClinit); - } - - /** - * Appends a method to the class. - */ - public void addMethod(MethodInfo minfo) throws CannotCompileException { - testExistingMethod(minfo.getName(), minfo.getDescriptor()); - methods.add(minfo); - } - - private void addMethod0(MethodInfo minfo) { - methods.add(minfo); - } - - private void testExistingMethod(String name, String descriptor) - throws CannotCompileException - { - ListIterator it = methods.listIterator(0); - while (it.hasNext()) { - MethodInfo minfo = (MethodInfo)it.next(); - if (minfo.getName().equals(name) - && Descriptor.eqParamTypes(minfo.getDescriptor(), descriptor)) - throw new CannotCompileException("duplicate method: " + name); - } - } - - /** - * Returns all the attributes. - * - * @return a list of AttributeInfo objects. - * @see AttributeInfo - */ - public List getAttributes() { - return attributes; - } - - /** - * Returns the attribute with the specified name. - * - * @param name attribute name - */ - public AttributeInfo getAttribute(String name) { - LinkedList list = attributes; - int n = list.size(); - for (int i = 0; i < n; ++i) { - AttributeInfo ai = (AttributeInfo)list.get(i); - if (ai.getName().equals(name)) - return ai; - } - - return null; - } - - /** - * Appends an attribute. If there is already an attribute with - * the same name, the new one substitutes for it. - */ - public void addAttribute(AttributeInfo info) { - AttributeInfo.remove(attributes, info.getName()); - attributes.add(info); - } - - /** - * Returns the source file containing this class. - * - * @return null if this information is not available. - */ - public String getSourceFile() { - SourceFileAttribute sf - = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag); - if (sf == null) - return null; - else - return sf.getFileName(); - } - - private void read(DataInputStream in) throws IOException { - int i, n; - int magic = in.readInt(); - if (magic != 0xCAFEBABE) - throw new IOException("non class file"); - - minor = in.readUnsignedShort(); - major = in.readUnsignedShort(); - constPool = new ConstPool(in); - accessFlags = in.readUnsignedShort(); - thisClass = in.readUnsignedShort(); - constPool.setThisClassInfo(thisClass); - superClass = in.readUnsignedShort(); - n = in.readUnsignedShort(); - if (n == 0) - interfaces = null; - else { - interfaces = new int[n]; - for (i = 0; i < n; ++i) - interfaces[i] = in.readUnsignedShort(); - } - - ConstPool cp = constPool; - n = in.readUnsignedShort(); - fields = new ArrayList(); - for (i = 0; i < n; ++i) - addField0(new FieldInfo(cp, in)); - - n = in.readUnsignedShort(); - methods = new ArrayList(); - for (i = 0; i < n; ++i) - addMethod0(new MethodInfo(cp, in)); - - attributes = new LinkedList(); - n = in.readUnsignedShort(); - for (i = 0; i < n; ++i) - addAttribute(AttributeInfo.read(cp, in)); - - thisclassname = constPool.getClassInfo(thisClass); - } - - /** - * Writes a class file represened by this object - * into an output stream. - */ - public void write(DataOutputStream out) throws IOException { - int i, n; - - out.writeInt(0xCAFEBABE); // magic - out.writeShort(minor); // minor version - out.writeShort(major); // major version - constPool.write(out); // constant pool - out.writeShort(accessFlags); - out.writeShort(thisClass); - out.writeShort(superClass); - - if (interfaces == null) - n = 0; - else - n = interfaces.length; - - out.writeShort(n); - for (i = 0; i < n; ++i) - out.writeShort(interfaces[i]); - - ArrayList list = fields; - n = list.size(); - out.writeShort(n); - for (i = 0; i < n; ++i) { - FieldInfo finfo = (FieldInfo)list.get(i); - finfo.write(out); - } - - list = methods; - n = list.size(); - out.writeShort(n); - for (i = 0; i < n; ++i) { - MethodInfo minfo = (MethodInfo)list.get(i); - minfo.write(out); - } - - out.writeShort(attributes.size()); - AttributeInfo.writeAll(attributes, out); - } - - /** - * Get the Major version. - * - * @return the major version - */ - public int getMajorVersion() { - return major; - } - - /** - * Set the Major version. - * - * @param major the major version - */ - public void setMajorVersion(int major) { - this.major = major; - } - - /** - * Get the Minor version. - * - * @return the minor version - */ - public int getMinorVersion() { - return minor; - } - - /** - * Set the Minor version. - * - * @param minor the minor version - */ - public void setMinorVersion(int minor) { - this.minor = minor; - } +public final class ClassFile +{ + int major, minor; // version number + ConstPool constPool; + int thisClass; + int accessFlags; + int superClass; + int[] interfaces; + ArrayList fields; + ArrayList methods; + LinkedList attributes; + + String thisclassname; // not JVM-internal name + String[] cachedInterfaces; + String cachedSuperclass; + + /** + * Constructs a class file from a byte stream. + */ + public ClassFile(DataInputStream in) throws IOException + { + read(in); + } + + /** + * Constructs a class file including no members. + * + * @param isInterface true if this is an interface. + * false if this is a class. + * @param classname a fully-qualified class name + * @param superclass a fully-qualified super class name + */ + public ClassFile(boolean isInterface, + String classname, String superclass) + { + major = 45; + minor = 3; // JDK 1.1 or later + constPool = new ConstPool(classname); + thisClass = constPool.getThisClassInfo(); + if (isInterface) + accessFlags = AccessFlag.SUPER | AccessFlag.INTERFACE + | AccessFlag.ABSTRACT; + else + accessFlags = AccessFlag.SUPER; + + initSuperclass(superclass); + interfaces = null; + fields = new ArrayList(); + methods = new ArrayList(); + thisclassname = classname; + + attributes = new LinkedList(); + attributes.add(new SourceFileAttribute(constPool, + getSourcefileName(thisclassname))); + } + + private void initSuperclass(String superclass) + { + if (superclass != null) + { + this.superClass = constPool.addClassInfo(superclass); + cachedSuperclass = superclass; + } + else + { + this.superClass = constPool.addClassInfo("java.lang.Object"); + cachedSuperclass = "java.lang.Object"; + } + } + + private static String getSourcefileName(String qname) + { + int index = qname.lastIndexOf('.'); + if (index >= 0) + qname = qname.substring(index + 1); + + return qname + ".java"; + } + + /** + * Eliminates dead constant pool items. If a method or a field is removed, + * the constant pool items used by that method/field become dead items. + * This method recreates a constant pool. + */ + public void compact() + { + ConstPool cp = compact0(); + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) + { + MethodInfo minfo = (MethodInfo) list.get(i); + minfo.compact(cp); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) + { + FieldInfo finfo = (FieldInfo) list.get(i); + finfo.compact(cp); + } + + attributes = AttributeInfo.copyAll(attributes, cp); + constPool = cp; + } + + private ConstPool compact0() + { + ConstPool cp = new ConstPool(thisclassname); + thisClass = cp.getThisClassInfo(); + String sc = getSuperclass(); + if (sc != null) + { + 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])); + } + + return cp; + } + + /** + * 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 = compact0(); + AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag); + LinkedList newAttributes = new LinkedList(); + if (invisibleAnnotations != null) + { + invisibleAnnotations = invisibleAnnotations.copy(cp, null); + newAttributes.add(invisibleAnnotations); + } + AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag); + if (visibleAnnotations != null) + { + visibleAnnotations = visibleAnnotations.copy(cp, null); + newAttributes.add(visibleAnnotations); + } + + 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 = newAttributes; + cp.prune(); + constPool = cp; + } + + /** + * Returns a constant pool table. + */ + public ConstPool getConstPool() + { + return constPool; + } + + /** + * Returns true if this is an interface. + */ + public boolean isInterface() + { + return (accessFlags & AccessFlag.INTERFACE) != 0; + } + + /** + * Returns true if this is a final class or interface. + */ + public boolean isFinal() + { + return (accessFlags & AccessFlag.FINAL) != 0; + } + + /** + * Returns true if this is an abstract class or an interface. + */ + public boolean isAbstract() + { + return (accessFlags & AccessFlag.ABSTRACT) != 0; + } + + /** + * Returns access flags. + * + * @see javassist.bytecode.AccessFlag + */ + public int getAccessFlags() + { + return accessFlags; + } + + /** + * Changes access flags. + * + * @see javassist.bytecode.AccessFlag + */ + public void setAccessFlags(int acc) + { + accessFlags = acc | AccessFlag.SUPER; + } + + /** + * Returns the class name. + */ + public String getName() + { + return thisclassname; + } + + /** + * Sets the class name. This method substitutes the new name + * for all occurrences of the old class name in the class file. + */ + public void setName(String name) + { + renameClass(thisclassname, name); + } + + /** + * Returns the super class name. + */ + public String getSuperclass() + { + if (cachedSuperclass == null) cachedSuperclass = constPool.getClassInfo(superClass); + return cachedSuperclass; + } + + /** + * Returns the index of the constant pool entry representing + * the super class. + */ + public int getSuperclassId() + { + return superClass; + } + + /** + * Sets the super class. + *

+ *

This method modifies constructors so that they call + * constructors declared in the new super class. + */ + public void setSuperclass(String superclass) + throws CannotCompileException + { + if (superclass == null) + superclass = "java.lang.Object"; + + try + { + this.superClass = constPool.addClassInfo(superclass); + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) + { + MethodInfo minfo = (MethodInfo) list.get(i); + minfo.setSuperclass(superclass); + } + } + catch (BadBytecode e) + { + throw new CannotCompileException(e); + } + cachedSuperclass = superclass; + } + + /** + * Replaces all occurrences of a class name in the class file. + *

+ *

If class X is substituted for class Y in the class file, + * X and Y must have the same signature. If Y provides a method + * m(), X must provide it even if X inherits m() from the super class. + * If this fact is not guaranteed, the bytecode verifier may cause + * an error. + * + * @param oldname the replaced class name + * @param newname the substituted class name + */ + public final void renameClass(String oldname, String newname) + { + ArrayList list; + int n; + + if (oldname.equals(newname)) + return; + + if (oldname.equals(thisclassname)) + thisclassname = newname; + + oldname = Descriptor.toJvmName(oldname); + newname = Descriptor.toJvmName(newname); + constPool.renameClass(oldname, newname); + + list = methods; + n = list.size(); + for (int i = 0; i < n; ++i) + { + MethodInfo minfo = (MethodInfo) list.get(i); + String desc = minfo.getDescriptor(); + minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) + { + FieldInfo finfo = (FieldInfo) list.get(i); + String desc = finfo.getDescriptor(); + finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); + } + } + + /** + * Replaces all occurrences of several class names in the class file. + * + * @param classnames specifies which class name is replaced + * with which new name. Class names must + * be described with the JVM-internal + * representation like + * java/lang/Object. + * @see #renameClass(String,String) + */ + public final void renameClass(Map classnames) + { + String jvmNewThisName + = (String) classnames.get(Descriptor.toJvmName(thisclassname)); + if (jvmNewThisName != null) + thisclassname = Descriptor.toJavaName(jvmNewThisName); + + constPool.renameClass(classnames); + + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) + { + MethodInfo minfo = (MethodInfo) list.get(i); + String desc = minfo.getDescriptor(); + minfo.setDescriptor(Descriptor.rename(desc, classnames)); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) + { + FieldInfo finfo = (FieldInfo) list.get(i); + String desc = finfo.getDescriptor(); + finfo.setDescriptor(Descriptor.rename(desc, classnames)); + } + } + + /** + * Returns the names of the interfaces implemented by the class. + */ + public String[] getInterfaces() + { + if (cachedInterfaces != null) return cachedInterfaces; + String[] rtn = null; + if (interfaces == null) + rtn = new String[0]; + else + { + int n = interfaces.length; + String[] list = new String[n]; + for (int i = 0; i < n; ++i) + list[i] = constPool.getClassInfo(interfaces[i]); + + rtn = list; + } + cachedInterfaces = rtn; + return rtn; + } + + /** + * Sets the interfaces. + * + * @param nameList the names of the interfaces. + */ + public void setInterfaces(String[] nameList) + { + cachedInterfaces = null; + if (nameList != null) + { + int n = nameList.length; + interfaces = new int[n]; + for (int i = 0; i < n; ++i) + interfaces[i] = constPool.addClassInfo(nameList[i]); + } + } + + /** + * Appends an interface to the + * interfaces implemented by the class. + */ + public void addInterface(String name) + { + cachedInterfaces = null; + int info = constPool.addClassInfo(name); + if (interfaces == null) + { + interfaces = new int[1]; + interfaces[0] = info; + } + else + { + int n = interfaces.length; + int[] newarray = new int[n + 1]; + System.arraycopy(interfaces, 0, newarray, 0, n); + newarray[n] = info; + interfaces = newarray; + } + } + + /** + * Returns all the fields declared in the class. + * + * @return a list of FieldInfo. + * @see FieldInfo + */ + public List getFields() + { + return fields; + } + + /** + * Appends a field to the class. + */ + public void addField(FieldInfo finfo) throws CannotCompileException + { + testExistingField(finfo.getName(), finfo.getDescriptor()); + fields.add(finfo); + } + + private void addField0(FieldInfo finfo) + { + fields.add(finfo); + } + + private void testExistingField(String name, String descriptor) + throws CannotCompileException + { + ListIterator it = fields.listIterator(0); + while (it.hasNext()) + { + FieldInfo minfo = (FieldInfo) it.next(); + if (minfo.getName().equals(name)) + throw new CannotCompileException("duplicate field: " + name); + } + } + + /** + * Returns all the methods declared in the class. + * + * @return a list of MethodInfo. + * @see MethodInfo + */ + public List getMethods() + { + return methods; + } + + /** + * Returns the method with the specified name. If there are multiple + * methods with that name, this method returns one of them. + * + * @return null if no such a method is found. + */ + public MethodInfo getMethod(String name) + { + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) + { + MethodInfo minfo = (MethodInfo) list.get(i); + if (minfo.getName().equals(name)) + return minfo; + } + + return null; + } + + /** + * Returns a static initializer (class initializer), or null if + * it does not exist. + */ + public MethodInfo getStaticInitializer() + { + return getMethod(MethodInfo.nameClinit); + } + + /** + * Appends a method to the class. + */ + public void addMethod(MethodInfo minfo) throws CannotCompileException + { + testExistingMethod(minfo.getName(), minfo.getDescriptor()); + methods.add(minfo); + } + + private void addMethod0(MethodInfo minfo) + { + methods.add(minfo); + } + + private void testExistingMethod(String name, String descriptor) + throws CannotCompileException + { + ListIterator it = methods.listIterator(0); + while (it.hasNext()) + { + MethodInfo minfo = (MethodInfo) it.next(); + if (minfo.getName().equals(name) + && Descriptor.eqParamTypes(minfo.getDescriptor(), descriptor)) + throw new CannotCompileException("duplicate method: " + name); + } + } + + /** + * Returns all the attributes. + * + * @return a list of AttributeInfo objects. + * @see AttributeInfo + */ + public List getAttributes() + { + return attributes; + } + + /** + * Returns the attribute with the specified name. + * + * @param name attribute name + */ + public AttributeInfo getAttribute(String name) + { + LinkedList list = attributes; + int n = list.size(); + for (int i = 0; i < n; ++i) + { + AttributeInfo ai = (AttributeInfo) list.get(i); + if (ai.getName().equals(name)) + return ai; + } + + return null; + } + + /** + * Appends an attribute. If there is already an attribute with + * the same name, the new one substitutes for it. + */ + public void addAttribute(AttributeInfo info) + { + AttributeInfo.remove(attributes, info.getName()); + attributes.add(info); + } + + /** + * Returns the source file containing this class. + * + * @return null if this information is not available. + */ + public String getSourceFile() + { + SourceFileAttribute sf + = (SourceFileAttribute) getAttribute(SourceFileAttribute.tag); + if (sf == null) + return null; + else + return sf.getFileName(); + } + + private void read(DataInputStream in) throws IOException + { + int i, n; + int magic = in.readInt(); + if (magic != 0xCAFEBABE) + throw new IOException("non class file"); + + minor = in.readUnsignedShort(); + major = in.readUnsignedShort(); + constPool = new ConstPool(in); + accessFlags = in.readUnsignedShort(); + thisClass = in.readUnsignedShort(); + constPool.setThisClassInfo(thisClass); + superClass = in.readUnsignedShort(); + n = in.readUnsignedShort(); + if (n == 0) + interfaces = null; + else + { + interfaces = new int[n]; + for (i = 0; i < n; ++i) + interfaces[i] = in.readUnsignedShort(); + } + + ConstPool cp = constPool; + n = in.readUnsignedShort(); + fields = new ArrayList(); + for (i = 0; i < n; ++i) + addField0(new FieldInfo(cp, in)); + + n = in.readUnsignedShort(); + methods = new ArrayList(); + for (i = 0; i < n; ++i) + addMethod0(new MethodInfo(cp, in)); + + attributes = new LinkedList(); + n = in.readUnsignedShort(); + for (i = 0; i < n; ++i) + addAttribute(AttributeInfo.read(cp, in)); + + thisclassname = constPool.getClassInfo(thisClass); + } + + /** + * Writes a class file represened by this object + * into an output stream. + */ + public void write(DataOutputStream out) throws IOException + { + int i, n; + + out.writeInt(0xCAFEBABE); // magic + out.writeShort(minor); // minor version + out.writeShort(major); // major version + constPool.write(out); // constant pool + out.writeShort(accessFlags); + out.writeShort(thisClass); + out.writeShort(superClass); + + if (interfaces == null) + n = 0; + else + n = interfaces.length; + + out.writeShort(n); + for (i = 0; i < n; ++i) + out.writeShort(interfaces[i]); + + ArrayList list = fields; + n = list.size(); + out.writeShort(n); + for (i = 0; i < n; ++i) + { + FieldInfo finfo = (FieldInfo) list.get(i); + finfo.write(out); + } + + list = methods; + n = list.size(); + out.writeShort(n); + for (i = 0; i < n; ++i) + { + MethodInfo minfo = (MethodInfo) list.get(i); + minfo.write(out); + } + + out.writeShort(attributes.size()); + AttributeInfo.writeAll(attributes, out); + } + + /** + * Get the Major version. + * + * @return the major version + */ + public int getMajorVersion() + { + return major; + } + + /** + * Set the Major version. + * + * @param major the major version + */ + public void setMajorVersion(int major) + { + this.major = major; + } + + /** + * Get the Minor version. + * + * @return the minor version + */ + public int getMinorVersion() + { + return minor; + } + + /** + * Set the Minor version. + * + * @param minor the minor version + */ + public void setMinorVersion(int minor) + { + this.minor = minor; + } } diff --git a/src/main/javassist/bytecode/FieldInfo.java b/src/main/javassist/bytecode/FieldInfo.java index 02c73a32..21ec3ffb 100644 --- a/src/main/javassist/bytecode/FieldInfo.java +++ b/src/main/javassist/bytecode/FieldInfo.java @@ -30,6 +30,8 @@ public final class FieldInfo { ConstPool constPool; int accessFlags; int name; + String cachedName; + String cachedType; int descriptor; LinkedList attribute; // may be null. @@ -51,6 +53,7 @@ public final class FieldInfo { public FieldInfo(ConstPool cp, String fieldName, String desc) { this(cp); name = cp.addUtf8Info(fieldName); + cachedName = fieldName; descriptor = cp.addUtf8Info(desc); } @@ -75,12 +78,24 @@ public final class FieldInfo { } void prune(ConstPool cp) { + AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag); + LinkedList newAttributes = new LinkedList(); + if (invisibleAnnotations != null) + { + invisibleAnnotations = invisibleAnnotations.copy(cp, null); + newAttributes.add(invisibleAnnotations); + } + AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag); + if (visibleAnnotations != null) + { + visibleAnnotations = visibleAnnotations.copy(cp, null); + newAttributes.add(visibleAnnotations); + } int index = getConstantValue(); - if (index == 0) - attribute = null; - else { + attribute = newAttributes; + if (index != 0) + { index = constPool.copy(index, cp, null); - attribute = new LinkedList(); attribute.add(new ConstantAttribute(cp, index)); } @@ -101,7 +116,8 @@ public final class FieldInfo { * Returns the field name. */ public String getName() { - return constPool.getUtf8Info(name); + if (cachedName == null) cachedName = constPool.getUtf8Info(name); + return cachedName; } /** @@ -109,6 +125,7 @@ public final class FieldInfo { */ public void setName(String newName) { name = constPool.addUtf8Info(newName); + cachedName = newName; } /** diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java index ffc7fcfc..ef94be44 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -32,6 +32,7 @@ public final class MethodInfo { ConstPool constPool; int accessFlags; int name; + String cachedName; int descriptor; LinkedList attribute; // may be null @@ -70,6 +71,7 @@ public final class MethodInfo { this(cp); accessFlags = 0; name = cp.addUtf8Info(methodname); + cachedName = methodname; descriptor = constPool.addUtf8Info(desc); } @@ -107,7 +109,7 @@ public final class MethodInfo { * Returns a string representation of the object. */ public String toString() { - return constPool.getUtf8Info(name) + " " + return getName() + " " + constPool.getUtf8Info(descriptor); } @@ -127,7 +129,23 @@ public final class MethodInfo { } void prune(ConstPool cp) { - attribute = null; + AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag); + LinkedList newAttributes = new LinkedList(); + if (invisibleAnnotations != null) + { + invisibleAnnotations = invisibleAnnotations.copy(cp, null); + newAttributes.add(invisibleAnnotations); + } + AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag); + if (visibleAnnotations != null) + { + visibleAnnotations = visibleAnnotations.copy(cp, null); + newAttributes.add(visibleAnnotations); + } + ExceptionsAttribute ea = getExceptionsAttribute(); + if (ea != null) newAttributes.add(ea); + + attribute = newAttributes; name = cp.addUtf8Info(getName()); descriptor = cp.addUtf8Info(getDescriptor()); constPool = cp; @@ -137,7 +155,8 @@ public final class MethodInfo { * Returns a method name. */ public String getName() { - return constPool.getUtf8Info(name); + if (cachedName == null) cachedName = constPool.getUtf8Info(name); + return cachedName; } /** @@ -145,6 +164,7 @@ public final class MethodInfo { */ public void setName(String newName) { name = constPool.addUtf8Info(newName); + cachedName = newName; } /** @@ -383,7 +403,7 @@ public final class MethodInfo { ConstPool destCp = constPool; accessFlags = src.accessFlags; name = destCp.addUtf8Info(methodname); - + cachedName = methodname; ConstPool srcCp = src.constPool; String desc = srcCp.getUtf8Info(src.descriptor); String desc2 = Descriptor.rename(desc, classnames); -- 2.39.5