diff options
author | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2004-09-28 17:06:56 +0000 |
---|---|---|
committer | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2004-09-28 17:06:56 +0000 |
commit | aca939f8d7b64c8636d7200021a830d15d5eb725 (patch) | |
tree | 0834a8cdeab7c26c2ce5ec19700bdc7dacd188d8 | |
parent | 02c99beb983350fa2ae2808ea35c9416cd63db5b (diff) | |
download | javassist-aca939f8d7b64c8636d7200021a830d15d5eb725.tar.gz javassist-aca939f8d7b64c8636d7200021a830d15d5eb725.zip |
reformatted the files edited by Bill.
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@138 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
-rw-r--r-- | build.xml | 1 | ||||
-rw-r--r-- | src/main/javassist/ClassPool.java | 1359 | ||||
-rw-r--r-- | src/main/javassist/CtClass.java | 2185 | ||||
-rw-r--r-- | src/main/javassist/CtClassType.java | 2256 | ||||
-rw-r--r-- | src/main/javassist/bytecode/AnnotationsAttribute.java | 1107 | ||||
-rw-r--r-- | src/main/javassist/bytecode/MethodInfo.java | 751 | ||||
-rw-r--r-- | src/main/javassist/bytecode/annotation/ClassMemberValue.java | 134 |
7 files changed, 3686 insertions, 4107 deletions
@@ -96,6 +96,7 @@ Copyright (C) 1999-2004 Shigeru Chiba. All Rights Reserved.</i>]]></bottom> <zip zipfile="${dist-version}.zip"> <zipfileset dir="${basedir}" prefix="${dist-version}"> <include name="**"/> + <exclude name=".*"/> <exclude name="build/**"/> <exclude name="local/**"/> <exclude name="${dist-version}.zip"/> diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index b0a2066c..50143d6a 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -31,9 +31,9 @@ import java.util.Hashtable; * to find a class file and then it creates a <code>CtClass</code> object * representing that class file. The created object is returned to the * caller. - * <p/> + * * <p><b>Memory consumption memo:</b> - * <p/> + * * <p><code>ClassPool</code> objects hold all the <code>CtClass</code>es * that have been created so that the consistency among modified classes * can be guaranteed. Thus if a large number of <code>CtClass</code>es @@ -43,9 +43,9 @@ import java.util.Hashtable; * 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/> + * * <p><b><code>ClassPool</code> hierarchy:</b> - * <p/> + * * <p><code>ClassPool</code>s can make a parent-child hierarchy as * <code>java.lang.ClassLoader</code>s. If a <code>ClassPool</code> has * a parent pool, <code>get()</code> first asks the parent pool to find @@ -57,709 +57,660 @@ import java.util.Hashtable; * @see javassist.CtClass * @see javassist.ClassPath */ -public class ClassPool -{ - - /** - * Determines the search order. - * <p/> - * <p>If this field is true, <code>get()</code> first searches the - * class path associated to this <code>ClassPool</code> and then - * the class path associated with the parent <code>ClassPool</code>. - * Otherwise, the class path associated with the parent is searched - * first. - * <p/> - * <p>The default value is false. - */ - public boolean childFirstLookup = false; - - protected ClassPoolTail source; - protected ClassPool parent; - protected Hashtable classes; // should be synchronous - - /** - * Table of registered cflow variables. - */ - 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. - */ - public ClassPool() - { - this(null); - } - - /** - * Creates a class pool. - * - * @param parent the parent of this class pool. If this is a root - * class pool, this parameter must be <code>null</code>. - * @see javassist.ClassPool#getDefault() - */ - public ClassPool(ClassPool parent) - { - this.classes = new Hashtable(INIT_HASH_SIZE); - this.source = new ClassPoolTail(); - this.parent = parent; - if (parent == null) - { - CtClass[] pt = CtClass.primitiveTypes; - for (int i = 0; i < pt.length; ++i) - classes.put(pt[i].getName(), pt[i]); - } - - this.cflow = null; - } - - /** - * Returns the default class pool. - * The returned object is always identical since this method is - * a singleton factory. - * <p/> - * <p>The default class pool searches the system search path, - * which usually includes the platform library, extension - * libraries, and the search path specified by the - * <code>-classpath</code> option or the <code>CLASSPATH</code> - * environment variable. - * <p/> - * <p>When this method is called for the first time, the default - * class pool is created with the following code snippet: - * <p/> - * <ul><code>ClassPool cp = new ClassPool(); - * cp.appendSystemPath(); - * </code></ul> - * <p/> - * <p>If the default class pool cannot find any class files, - * try <code>ClassClassPath</code> and <code>LoaderClassPath</code>. - * - * @see ClassClassPath - * @see LoaderClassPath - */ - public static synchronized ClassPool getDefault() - { - if (defaultPool == null) - { - defaultPool = new ClassPool(null); - defaultPool.appendSystemPath(); - } - - return defaultPool; - } - - private static ClassPool defaultPool = null; - - /** - * Provide a hook so that subclasses can do their own - * caching of classes. - * - * @see #cacheCtClass(String,CtClass) - * @see #removeCached(String) - */ - protected CtClass getCached(String classname) - { - return (CtClass) classes.get(classname); - } - - /** - * Provides a hook so that subclasses can do their own - * caching of classes. - * - * @see #getCached(String) - * @see #removeCached(String,CtClass) - */ - protected void cacheCtClass(String classname, CtClass c) - { - classes.put(classname, c); - } - - /** - * Provide a hook so that subclasses can do their own - * caching of classes. - * - * @see #getCached(String) - * @see #cacheCtClass(String,CtClass) - */ - protected CtClass removeCached(String classname) - { - return (CtClass) classes.remove(classname); - } - - /** - * Returns the class search path. - */ - public String toString() - { - return source.toString(); - } - - /** - * Records a name that never exists. - * For example, a package name can be recorded by this method. - * This would improve execution performance - * since <code>get()</code> does not search the class path at all - * if the given name is an invalid name recorded by this method. - * Note that searching the class path takes relatively long time. - * - * @param name a class name (separeted by dot). - */ - public void recordInvalidClassName(String name) - { - source.recordInvalidClassName(name); - } - - /** - * Records the <code>$cflow</code> variable for the field specified - * by <code>cname</code> and <code>fname</code>. - * - * @param name variable name - * @param cname class name - * @param fname field name - */ - void recordCflow(String name, String cname, String fname) - { - if (cflow == null) - cflow = new Hashtable(); - - cflow.put(name, new Object[]{cname, fname}); - } - - /** - * Undocumented method. Do not use; internal-use only. - * - * @param name the name of <code>$cflow</code> variable - */ - public Object[] lookupCflow(String name) - { - if (cflow == null) - cflow = new Hashtable(); - - return (Object[]) cflow.get(name); - } - - /** - * Reads a class file and constructs a <code>CtClass</code> - * object with a new name. - * This method is useful if you want to generate a new class as a copy - * of another class (except the class name). For example, - * <p/> - * <ul><pre> - * getAndRename("Point", "Pair") - * </pre></ul> - * <p/> - * returns a <code>CtClass</code> object representing <code>Pair</code> - * class. The definition of <code>Pair</code> is the same as that of - * <code>Point</code> class except the class name since <code>Pair</code> - * is defined by reading <code>Point.class</code>. - * - * @param orgName the original (fully-qualified) class name - * @param newName the new class name - */ - public CtClass getAndRename(String orgName, String newName) - throws NotFoundException - { - CtClass clazz = get0(orgName, false); - if (clazz instanceof CtClassType) - ((CtClassType) clazz).setClassPool(this); - - clazz.setName(newName); // indirectly calls - // classNameChanged() in this class - return clazz; - } - - /* - * This method is invoked by CtClassType.setName(). It removes a - * CtClass object from the hash table and inserts it with the new - * name. Don't delegate to the parent. - */ - synchronized void classNameChanged(String oldname, CtClass clazz) - { - CtClass c = (CtClass) getCached(oldname); - if (c == clazz) // must check this equation. - removeCached(oldname); // see getAndRename(). - - String newName = clazz.getName(); - checkNotFrozen(newName); - cacheCtClass(newName, clazz); - } - - /** - * Reads a class file from the source and returns a reference - * to the <code>CtClass</code> - * object representing that class file. If that class file has been - * already read, this method returns a reference to the - * <code>CtClass</code> created when that class file was read at the - * first time. - * <p/> - * <p>If <code>classname</code> ends with "[]", then this method - * returns a <code>CtClass</code> object for that array type. - * <p/> - * <p>To obtain an inner class, use "$" instead of "." for separating - * the enclosing class name and the inner class name. - * - * @param classname a fully-qualified class name. - */ - public CtClass get(String classname) throws NotFoundException - { - CtClass clazz; - if (classname == null) - clazz = null; - else - clazz = get0(classname, true); - - if (clazz == null) - throw new NotFoundException(classname); - else - { - clazz.incGetCounter(); - return clazz; - } - } - - /** - * @param useCache false if the cached CtClass must be ignored. - * @param searchParent false if the parent class pool is not searched. - * @return null if the class could not be found. - */ - protected synchronized CtClass get0(String classname, boolean useCache) - throws NotFoundException - { - CtClass clazz = null; - if (useCache) - { - clazz = getCached(classname); - if (clazz != null) +public class ClassPool { + + /** + * Determines the search order. + * + * <p>If this field is true, <code>get()</code> first searches the + * class path associated to this <code>ClassPool</code> and then + * the class path associated with the parent <code>ClassPool</code>. + * Otherwise, the class path associated with the parent is searched + * first. + * + * <p>The default value is false. + */ + public boolean childFirstLookup = false; + + protected ClassPoolTail source; + protected ClassPool parent; + protected Hashtable classes; // should be synchronous + + /** + * Table of registered cflow variables. + */ + 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. + */ + public ClassPool() { + this(null); + } + + /** + * Creates a class pool. + * + * @param parent the parent of this class pool. If this is a root + * class pool, this parameter must be <code>null</code>. + * @see javassist.ClassPool#getDefault() + */ + public ClassPool(ClassPool parent) { + this.classes = new Hashtable(INIT_HASH_SIZE); + this.source = new ClassPoolTail(); + this.parent = parent; + if (parent == null) { + CtClass[] pt = CtClass.primitiveTypes; + for (int i = 0; i < pt.length; ++i) + classes.put(pt[i].getName(), pt[i]); + } + + this.cflow = null; + } + + /** + * Returns the default class pool. + * The returned object is always identical since this method is + * a singleton factory. + * + * <p>The default class pool searches the system search path, + * which usually includes the platform library, extension + * libraries, and the search path specified by the + * <code>-classpath</code> option or the <code>CLASSPATH</code> + * environment variable. + * + * <p>When this method is called for the first time, the default + * class pool is created with the following code snippet: + * + * <ul><code>ClassPool cp = new ClassPool(); + * cp.appendSystemPath(); + * </code></ul> + * + * <p>If the default class pool cannot find any class files, + * try <code>ClassClassPath</code> and <code>LoaderClassPath</code>. + * + * @see ClassClassPath + * @see LoaderClassPath + */ + public static synchronized ClassPool getDefault() { + if (defaultPool == null) { + defaultPool = new ClassPool(null); + defaultPool.appendSystemPath(); + } + + return defaultPool; + } + + private static ClassPool defaultPool = null; + + /** + * Provide a hook so that subclasses can do their own + * caching of classes. + * + * @see #cacheCtClass(String,CtClass) + * @see #removeCached(String) + */ + protected CtClass getCached(String classname) { + return (CtClass)classes.get(classname); + } + + /** + * Provides a hook so that subclasses can do their own + * caching of classes. + * + * @see #getCached(String) + * @see #removeCached(String,CtClass) + */ + protected void cacheCtClass(String classname, CtClass c) { + classes.put(classname, c); + } + + /** + * Provide a hook so that subclasses can do their own + * caching of classes. + * + * @see #getCached(String) + * @see #cacheCtClass(String,CtClass) + */ + protected CtClass removeCached(String classname) { + return (CtClass)classes.remove(classname); + } + + /** + * Returns the class search path. + */ + public String toString() { + return source.toString(); + } + + /** + * Records a name that never exists. + * For example, a package name can be recorded by this method. + * This would improve execution performance + * since <code>get()</code> does not search the class path at all + * if the given name is an invalid name recorded by this method. + * Note that searching the class path takes relatively long time. + * + * @param name a class name (separeted by dot). + */ + public void recordInvalidClassName(String name) { + source.recordInvalidClassName(name); + } + + /** + * Records the <code>$cflow</code> variable for the field specified + * by <code>cname</code> and <code>fname</code>. + * + * @param name variable name + * @param cname class name + * @param fname field name + */ + void recordCflow(String name, String cname, String fname) { + if (cflow == null) + cflow = new Hashtable(); + + cflow.put(name, new Object[] { cname, fname }); + } + + /** + * Undocumented method. Do not use; internal-use only. + * + * @param name the name of <code>$cflow</code> variable + */ + public Object[] lookupCflow(String name) { + if (cflow == null) + cflow = new Hashtable(); + + return (Object[])cflow.get(name); + } + + /** + * Reads a class file and constructs a <code>CtClass</code> + * object with a new name. + * This method is useful if you want to generate a new class as a copy + * of another class (except the class name). For example, + * + * <ul><pre> + * getAndRename("Point", "Pair") + * </pre></ul> + * + * returns a <code>CtClass</code> object representing <code>Pair</code> + * class. The definition of <code>Pair</code> is the same as that of + * <code>Point</code> class except the class name since <code>Pair</code> + * is defined by reading <code>Point.class</code>. + * + * @param orgName the original (fully-qualified) class name + * @param newName the new class name + */ + public CtClass getAndRename(String orgName, String newName) + throws NotFoundException + { + CtClass clazz = get0(orgName, false); + if (clazz instanceof CtClassType) + ((CtClassType)clazz).setClassPool(this); + + clazz.setName(newName); // indirectly calls + // classNameChanged() in this class + return clazz; + } + + /* + * This method is invoked by CtClassType.setName(). It removes a + * CtClass object from the hash table and inserts it with the new + * name. Don't delegate to the parent. + */ + synchronized void classNameChanged(String oldname, CtClass clazz) { + CtClass c = (CtClass)getCached(oldname); + if (c == clazz) // must check this equation. + removeCached(oldname); // see getAndRename(). + + String newName = clazz.getName(); + checkNotFrozen(newName); + cacheCtClass(newName, clazz); + } + + /** + * Reads a class file from the source and returns a reference + * to the <code>CtClass</code> + * object representing that class file. If that class file has been + * already read, this method returns a reference to the + * <code>CtClass</code> created when that class file was read at the + * first time. + * + * <p>If <code>classname</code> ends with "[]", then this method + * returns a <code>CtClass</code> object for that array type. + * + * <p>To obtain an inner class, use "$" instead of "." for separating + * the enclosing class name and the inner class name. + * + * @param classname a fully-qualified class name. + */ + public CtClass get(String classname) throws NotFoundException { + CtClass clazz; + if (classname == null) + clazz = null; + else + clazz = get0(classname, true); + + if (clazz == null) + throw new NotFoundException(classname); + else { + clazz.incGetCounter(); return clazz; - } + } + } + + /** + * @param useCache false if the cached CtClass must be ignored. + * @param searchParent false if the parent class pool is not searched. + * @return null if the class could not be found. + */ + protected synchronized CtClass get0(String classname, boolean useCache) + throws NotFoundException + { + CtClass clazz = null; + if (useCache) { + clazz = getCached(classname); + if (clazz != null) + return clazz; + } + + if (!childFirstLookup && parent != null) { + clazz = parent.get0(classname, useCache); + if (clazz != null) + return clazz; + } + + clazz = createCtClass(classname, useCache); + if (clazz != null) { + if (useCache) + cacheCtClass(classname, clazz); - if (!childFirstLookup && parent != null) - { - clazz = parent.get0(classname, useCache); - if (clazz != null) return clazz; - } - - clazz = createCtClass(classname, useCache); - if (clazz != null) - { - if (useCache) - cacheCtClass(classname, clazz); - - return clazz; - } - - if (childFirstLookup && parent != null) - clazz = parent.get0(classname, useCache); - - return clazz; - } - - /** - * Creates a CtClass object representing the specified class. - * It first examines whether or not the corresponding class - * file exists. If yes, it creates a CtClass object. - * - * @return null if the class file could not be found. - */ - protected CtClass createCtClass(String classname, boolean useCache) - { - if (classname.endsWith("[]")) - { - String base = classname.substring(0, classname.indexOf('[')); - if ((!useCache || getCached(base) == null) && find(base) == null) - return null; - else - return new CtArray(classname, this); - } - else if (find(classname) == null) - return null; - else - return new CtClassType(classname, this); - } - - /** - * Searches the class path to obtain the URL of the class file - * specified by classname. It is also used to determine whether - * the class file exists. - * - * @param classname a fully-qualified class name. - * @return null if the class file could not be found. - * @see CtClass#getURL() - */ - public URL find(String classname) - { - return source.find(classname); - } - - /* - * Is invoked by CtClassType.setName() and methods in this class. - * This method throws an exception if the class is already frozen or - * if this class pool cannot edit the class since it is in a parent - * class pool. - */ - void checkNotFrozen(String classname) throws RuntimeException - { - CtClass clazz = getCached(classname); - if (clazz == null) - { - if (!childFirstLookup && parent != null) - { - try - { - clazz = parent.get0(classname, true); + } + + if (childFirstLookup && parent != null) + clazz = parent.get0(classname, useCache); + + return clazz; + } + + /** + * Creates a CtClass object representing the specified class. + * It first examines whether or not the corresponding class + * file exists. If yes, it creates a CtClass object. + * + * @return null if the class file could not be found. + */ + protected CtClass createCtClass(String classname, boolean useCache) { + if (classname.endsWith("[]")) { + String base = classname.substring(0, classname.indexOf('[')); + if ((!useCache || getCached(base) == null) && find(base) == null) + return null; + else + return new CtArray(classname, this); + } + else + if (find(classname) == null) + return null; + else + return new CtClassType(classname, this); + } + + /** + * Searches the class path to obtain the URL of the class file + * specified by classname. It is also used to determine whether + * the class file exists. + * + * @param classname a fully-qualified class name. + * @return null if the class file could not be found. + * @see CtClass#getURL() + */ + public URL find(String classname) { + return source.find(classname); + } + + /* + * Is invoked by CtClassType.setName() and methods in this class. + * This method throws an exception if the class is already frozen or + * if this class pool cannot edit the class since it is in a parent + * class pool. + */ + void checkNotFrozen(String classname) throws RuntimeException { + CtClass clazz = getCached(classname); + if (clazz == null) { + if (!childFirstLookup && parent != null) { + try { + clazz = parent.get0(classname, true); + } + catch (NotFoundException e) {} + if (clazz != null) + throw new RuntimeException(classname + + " is in a parent ClassPool. Use the parent."); } - catch (NotFoundException e) - { + } + else + if (clazz.isFrozen()) + throw new RuntimeException(classname + + ": frozen class (cannot edit)"); + } + + /* for CtClassType.getClassFile2(). Don't delegate to the parent. + */ + InputStream openClassfile(String classname) throws NotFoundException { + return source.openClassfile(classname); + } + + void writeClassfile(String classname, OutputStream out) + throws NotFoundException, IOException, CannotCompileException + { + source.writeClassfile(classname, out); + } + + /** + * Reads class files from the source and returns an array of + * <code>CtClass</code> + * objects representing those class files. + * + * <p>If an element of <code>classnames</code> ends with "[]", + * then this method + * returns a <code>CtClass</code> object for that array type. + * + * @param classnames an array of fully-qualified class name. + */ + public CtClass[] get(String[] classnames) throws NotFoundException { + if (classnames == null) + return new CtClass[0]; + + int num = classnames.length; + CtClass[] result = new CtClass[num]; + for (int i = 0; i < num; ++i) + result[i] = get(classnames[i]); + + return result; + } + + /** + * Reads a class file and obtains a compile-time method. + * + * @param classname the class name + * @param methodname the method name + * @see CtClass#getDeclaredMethod(String) + */ + public CtMethod getMethod(String classname, String methodname) + throws NotFoundException + { + CtClass c = get(classname); + return c.getDeclaredMethod(methodname); + } + + /** + * Creates a new class (or interface) from the given class file. + * If there already exists a class with the same name, the new class + * overwrites that previous class. + * + * <p>This method is used for creating a <code>CtClass</code> object + * directly from a class file. The qualified class name is obtained + * from the class file; you do not have to explicitly give the name. + * + * @param classfile class file. + * @throws RuntimeException if there is a frozen class with the + * the same name. + * @see javassist.ByteArrayClassPath + */ + public CtClass makeClass(InputStream classfile) + throws IOException, RuntimeException { + classfile = new BufferedInputStream(classfile); + CtClass clazz = new CtClassType(classfile, this); + clazz.checkModify(); + String classname = clazz.getName(); + checkNotFrozen(classname); + cacheCtClass(classname, clazz); + return clazz; + } + + /** + * Creates a new public class. + * If there already exists a class with the same name, the new class + * overwrites that previous class. + * + * @param classname a fully-qualified class name. + * @throws RuntimeException if the existing class is frozen. + */ + public CtClass makeClass(String classname) throws RuntimeException { + return makeClass(classname, null); + } + + /** + * Creates a new public class. + * If there already exists a class/interface with the same name, + * the new class overwrites that previous class. + * + * @param classname a fully-qualified class name. + * @param superclass the super class. + * @throws RuntimeException if the existing class is frozen. + */ + public synchronized CtClass makeClass(String classname, CtClass superclass) + throws RuntimeException + { + checkNotFrozen(classname); + CtClass clazz = new CtNewClass(classname, this, false, superclass); + cacheCtClass(classname, clazz); + return clazz; + } + + /** + * Creates a new nested class. + * This method is called by CtClassType.makeNestedClass(). + * + * @param classname a fully-qualified class name. + * @return the nested class. + */ + synchronized CtClass makeNestedClass(String classname) { + checkNotFrozen(classname); + CtClass clazz = new CtNewNestedClass(classname, this, false, null); + cacheCtClass(classname, clazz); + return clazz; + } + + /** + * Creates a new public interface. + * If there already exists a class/interface with the same name, + * the new interface overwrites that previous one. + * + * @param name a fully-qualified interface name. + * @throws RuntimeException if the existing interface is frozen. + */ + public CtClass makeInterface(String name) throws RuntimeException { + return makeInterface(name, null); + } + + /** + * Creates a new public interface. + * If there already exists a class/interface with the same name, + * the new interface overwrites that previous one. + * + * @param name a fully-qualified interface name. + * @param superclass the super interface. + * @throws RuntimeException if the existing interface is frozen. + */ + public synchronized CtClass makeInterface(String name, CtClass superclass) + throws RuntimeException + { + checkNotFrozen(name); + CtClass clazz = new CtNewClass(name, this, true, superclass); + cacheCtClass(name, clazz); + return clazz; + } + + /** + * Appends the system search path to the end of the + * search path. The system search path + * usually includes the platform library, extension + * libraries, and the search path specified by the + * <code>-classpath</code> option or the <code>CLASSPATH</code> + * environment variable. + * + * @return the appended class path. + */ + public ClassPath appendSystemPath() { + return source.appendSystemPath(); + } + + /** + * Insert a <code>ClassPath</code> object at the head of the + * search path. + * + * @return the inserted class path. + * @see javassist.ClassPath + * @see javassist.URLClassPath + * @see javassist.ByteArrayClassPath + */ + public ClassPath insertClassPath(ClassPath cp) { + return source.insertClassPath(cp); + } + + /** + * Appends a <code>ClassPath</code> object to the end of the + * search path. + * + * @return the appended class path. + * @see javassist.ClassPath + * @see javassist.URLClassPath + * @see javassist.ByteArrayClassPath + */ + public ClassPath appendClassPath(ClassPath cp) { + return source.appendClassPath(cp); + } + + /** + * Inserts a directory or a jar (or zip) file at the head of the + * search path. + * + * @param pathname the path name of the directory or jar file. + * It must not end with a path separator ("/"). + * @return the inserted class path. + * @throws NotFoundException if the jar file is not found. + */ + public ClassPath insertClassPath(String pathname) + throws NotFoundException + { + return source.insertClassPath(pathname); + } + + /** + * Appends a directory or a jar (or zip) file to the end of the + * search path. + * + * @param pathname the path name of the directory or jar file. + * It must not end with a path separator ("/"). + * @return the appended class path. + * @throws NotFoundException if the jar file is not found. + */ + public ClassPath appendClassPath(String pathname) + throws NotFoundException + { + return source.appendClassPath(pathname); + } + + /** + * Detatches the <code>ClassPath</code> object from the search path. + * The detached <code>ClassPath</code> object cannot be added + * to the pathagain. + */ + public void removeClassPath(ClassPath cp) { + source.removeClassPath(cp); + } + + /** + * Appends directories and jar files for search. + * + * <p>The elements of the given path list must be separated by colons + * in Unix or semi-colons in Windows. + * + * @param pathlist a (semi)colon-separated list of + * the path names of directories and jar files. + * The directory name must not end with a path + * separator ("/"). + * @throws NotFoundException if a jar file is not found. + */ + public void appendPathList(String pathlist) throws NotFoundException { + char sep = File.pathSeparatorChar; + int i = 0; + for (;;) { + int j = pathlist.indexOf(sep, i); + if (j < 0) { + appendClassPath(pathlist.substring(i)); + break; } - if (clazz != null) - throw new RuntimeException(classname - + " is in a parent ClassPool. Use the parent."); - } - } - else if (clazz.isFrozen()) - throw new RuntimeException(classname + - ": frozen class (cannot edit)"); - } - - /* for CtClassType.getClassFile2(). Don't delegate to the parent. - */ - InputStream openClassfile(String classname) - throws NotFoundException - { - return source.openClassfile(classname); - } - - void writeClassfile(String classname, OutputStream out) - throws NotFoundException, IOException, CannotCompileException - { - source.writeClassfile(classname, out); - } - - /** - * Reads class files from the source and returns an array of - * <code>CtClass</code> - * objects representing those class files. - * <p/> - * <p>If an element of <code>classnames</code> ends with "[]", - * then this method - * returns a <code>CtClass</code> object for that array type. - * - * @param classnames an array of fully-qualified class name. - */ - public CtClass[] get(String[] classnames) throws NotFoundException - { - if (classnames == null) - return new CtClass[0]; - - int num = classnames.length; - CtClass[] result = new CtClass[num]; - for (int i = 0; i < num; ++i) - result[i] = get(classnames[i]); - - return result; - } - - /** - * Reads a class file and obtains a compile-time method. - * - * @param classname the class name - * @param methodname the method name - * @see CtClass#getDeclaredMethod(String) - */ - public CtMethod getMethod(String classname, String methodname) - throws NotFoundException - { - CtClass c = get(classname); - return c.getDeclaredMethod(methodname); - } - - /** - * Creates a new class (or interface) from the given class file. - * If there already exists a class with the same name, the new class - * overwrites that previous class. - * <p/> - * <p>This method is used for creating a <code>CtClass</code> object - * directly from a class file. The qualified class name is obtained - * from the class file; you do not have to explicitly give the name. - * - * @param classfile class file. - * @throws RuntimeException if there is a frozen class with the - * the same name. - * @see javassist.ByteArrayClassPath - */ - public CtClass makeClass(InputStream classfile) - throws IOException, RuntimeException - { - classfile = new BufferedInputStream(classfile); - CtClass clazz = new CtClassType(classfile, this); - clazz.checkModify(); - String classname = clazz.getName(); - checkNotFrozen(classname); - cacheCtClass(classname, clazz); - return clazz; - } - - /** - * Creates a new public class. - * If there already exists a class with the same name, the new class - * overwrites that previous class. - * - * @param classname a fully-qualified class name. - * @throws RuntimeException if the existing class is frozen. - */ - public CtClass makeClass(String classname) throws RuntimeException - { - return makeClass(classname, null); - } - - /** - * Creates a new public class. - * If there already exists a class/interface with the same name, - * the new class overwrites that previous class. - * - * @param classname a fully-qualified class name. - * @param superclass the super class. - * @throws RuntimeException if the existing class is frozen. - */ - public synchronized CtClass makeClass(String classname, CtClass superclass) - throws RuntimeException - { - checkNotFrozen(classname); - CtClass clazz = new CtNewClass(classname, this, false, superclass); - cacheCtClass(classname, clazz); - return clazz; - } - - /** - * Creates a new nested class. - * This method is called by CtClassType.makeNestedClass(). - * - * @param classname a fully-qualified class name. - * @return the nested class. - */ - synchronized CtClass makeNestedClass(String classname) - { - checkNotFrozen(classname); - CtClass clazz = new CtNewNestedClass(classname, this, false, null); - cacheCtClass(classname, clazz); - return clazz; - } - - /** - * Creates a new public interface. - * If there already exists a class/interface with the same name, - * the new interface overwrites that previous one. - * - * @param name a fully-qualified interface name. - * @throws RuntimeException if the existing interface is frozen. - */ - public CtClass makeInterface(String name) throws RuntimeException - { - return makeInterface(name, null); - } - - /** - * Creates a new public interface. - * If there already exists a class/interface with the same name, - * the new interface overwrites that previous one. - * - * @param name a fully-qualified interface name. - * @param superclass the super interface. - * @throws RuntimeException if the existing interface is frozen. - */ - public synchronized CtClass makeInterface(String name, CtClass superclass) - throws RuntimeException - { - checkNotFrozen(name); - CtClass clazz = new CtNewClass(name, this, true, superclass); - cacheCtClass(name, clazz); - return clazz; - } - - /** - * Appends the system search path to the end of the - * search path. The system search path - * usually includes the platform library, extension - * libraries, and the search path specified by the - * <code>-classpath</code> option or the <code>CLASSPATH</code> - * environment variable. - * - * @return the appended class path. - */ - public ClassPath appendSystemPath() - { - return source.appendSystemPath(); - } - - /** - * Insert a <code>ClassPath</code> object at the head of the - * search path. - * - * @return the inserted class path. - * @see javassist.ClassPath - * @see javassist.URLClassPath - * @see javassist.ByteArrayClassPath - */ - public ClassPath insertClassPath(ClassPath cp) - { - return source.insertClassPath(cp); - } - - /** - * Appends a <code>ClassPath</code> object to the end of the - * search path. - * - * @return the appended class path. - * @see javassist.ClassPath - * @see javassist.URLClassPath - * @see javassist.ByteArrayClassPath - */ - public ClassPath appendClassPath(ClassPath cp) - { - return source.appendClassPath(cp); - } - - /** - * Inserts a directory or a jar (or zip) file at the head of the - * search path. - * - * @param pathname the path name of the directory or jar file. - * It must not end with a path separator ("/"). - * @return the inserted class path. - * @throws NotFoundException if the jar file is not found. - */ - public ClassPath insertClassPath(String pathname) - throws NotFoundException - { - return source.insertClassPath(pathname); - } - - /** - * Appends a directory or a jar (or zip) file to the end of the - * search path. - * - * @param pathname the path name of the directory or jar file. - * It must not end with a path separator ("/"). - * @return the appended class path. - * @throws NotFoundException if the jar file is not found. - */ - public ClassPath appendClassPath(String pathname) - throws NotFoundException - { - return source.appendClassPath(pathname); - } - - /** - * Detatches the <code>ClassPath</code> object from the search path. - * The detached <code>ClassPath</code> object cannot be added - * to the pathagain. - */ - public void removeClassPath(ClassPath cp) - { - source.removeClassPath(cp); - } - - /** - * Appends directories and jar files for search. - * <p/> - * <p>The elements of the given path list must be separated by colons - * in Unix or semi-colons in Windows. - * - * @param pathlist a (semi)colon-separated list of - * the path names of directories and jar files. - * The directory name must not end with a path - * separator ("/"). - * @throws NotFoundException if a jar file is not found. - */ - public void appendPathList(String pathlist) throws NotFoundException - { - char sep = File.pathSeparatorChar; - int i = 0; - for (; ;) - { - int j = pathlist.indexOf(sep, i); - if (j < 0) - { - appendClassPath(pathlist.substring(i)); - break; - } - else - { - appendClassPath(pathlist.substring(i, j)); - i = j + 1; - } - } - } - - /** - * Converts this class to a <code>java.lang.Class</code> object. - * Once this method is called, further modifications are not - * allowed any more. - * To load the class, this method uses the context class loader - * of the current thread. If the program is running on some application - * server, the context class loader might be inappropriate to load the - * class. - * <p/> - * <p>This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - * @see #toClass(java.lang.ClassLoader) - */ - public Class toClass(CtClass clazz) - throws CannotCompileException - { - return toClass(clazz, Thread.currentThread().getContextClassLoader()); - } - - /** - * Converts this class to a <code>java.lang.Class</code> object. - * Once this method is called, further modifications are not allowed - * any more. - * <p/> - * <p>The class file represented by this <code>CtClass</code> is - * loaded by the given class loader to construct a - * <code>java.lang.Class</code> object. Since a private method - * on the class loader is invoked through the reflection API, - * the caller must have permissions to do that. - * <p/> - * <p>This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - * @param loader the class loader used to load this class. - */ - public Class toClass(CtClass ct, ClassLoader loader) - throws CannotCompileException - { - try - { - byte[] b = ct.toBytecode(); - Class cl = Class.forName("java.lang.ClassLoader"); - java.lang.reflect.Method method - = cl.getDeclaredMethod("defineClass", - new Class[]{String.class, byte[].class, - int.class, int.class}); - method.setAccessible(true); - Object[] args = new Object[]{ct.getName(), b, new Integer(0), - new Integer(b.length)}; - Class clazz = (Class) method.invoke(loader, args); - method.setAccessible(false); - return clazz; - } - catch (RuntimeException e) - { - throw e; - } - catch (java.lang.reflect.InvocationTargetException e) - { - throw new CannotCompileException(e.getTargetException()); - } - catch (Exception e) - { - throw new CannotCompileException(e); - } - } - + else { + appendClassPath(pathlist.substring(i, j)); + i = j + 1; + } + } + } + + /** + * Converts the given class to a <code>java.lang.Class</code> object. + * Once this method is called, further modifications are not + * allowed any more. + * To load the class, this method uses the context class loader + * of the current thread. If the program is running on some application + * server, the context class loader might be inappropriate to load the + * class. + * + * <p>This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + * @see #toClass(CtClass, java.lang.ClassLoader) + */ + public Class toClass(CtClass clazz) throws CannotCompileException { + return toClass(clazz, Thread.currentThread().getContextClassLoader()); + } + + /** + * Converts the class to a <code>java.lang.Class</code> object. + * Once this method is called, further modifications are not allowed + * any more. + * + * <p>The class file represented by the given <code>CtClass</code> is + * loaded by the given class loader to construct a + * <code>java.lang.Class</code> object. Since a private method + * on the class loader is invoked through the reflection API, + * the caller must have permissions to do that. + * + * <p>This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + * @param loader the class loader used to load this class. + */ + public Class toClass(CtClass ct, ClassLoader loader) + throws CannotCompileException + { + try { + byte[] b = ct.toBytecode(); + Class cl = Class.forName("java.lang.ClassLoader"); + java.lang.reflect.Method method = + cl.getDeclaredMethod("defineClass", + new Class[] { String.class, byte[].class, + int.class, int.class }); + method.setAccessible(true); + Object[] args = new Object[] { ct.getName(), b, new Integer(0), + new Integer(b.length)}; + Class clazz = (Class)method.invoke(loader, args); + method.setAccessible(false); + return clazz; + } + catch (RuntimeException e) { + throw e; + } + catch (java.lang.reflect.InvocationTargetException e) { + throw new CannotCompileException(e.getTargetException()); + } + catch (Exception e) { + throw new CannotCompileException(e); + } + } } - diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 5a1805cc..29512ace 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -38,1141 +38,1052 @@ import java.util.Collection; * * @see ClassPool#get(String) */ -public abstract class CtClass -{ - protected String qualifiedName; - - /** - * The version number of this release. - */ - public static final String version = "3.0 RC1"; - - /** - * Prints the version number and the copyright notice. - * <p/> - * <p>The following command invokes this method: - * <p/> - * <ul><pre>java -jar javassist.jar</pre></ul> - */ - public static void main(String[] args) - { - System.out.println("Javassist version " + CtClass.version); - System.out.println("Copyright (C) 1999-2004 Shigeru Chiba." - + " All Rights Reserved."); - } - - static final String javaLangObject = "java.lang.Object"; - - /** - * The <code>CtClass</code> object representing - * the <code>boolean</code> type. - */ - public static CtClass booleanType; - - /** - * The <code>CtClass</code> object representing - * the <code>char</code> type. - */ - public static CtClass charType; - - /** - * The <code>CtClass</code> object representing - * the <code>byte</code> type. - */ - public static CtClass byteType; - - /** - * The <code>CtClass</code> object representing - * the <code>short</code> type. - */ - public static CtClass shortType; - - /** - * The <code>CtClass</code> object representing - * the <code>int</code> type. - */ - public static CtClass intType; - - /** - * The <code>CtClass</code> object representing - * the <code>long</code> type. - */ - public static CtClass longType; - - /** - * The <code>CtClass</code> object representing - * the <code>float</code> type. - */ - public static CtClass floatType; - - /** - * The <code>CtClass</code> object representing - * the <code>double</code> type. - */ - public static CtClass doubleType; - - /** - * The <code>CtClass</code> object representing - * the <code>void</code> type. - */ - public static CtClass voidType; - - static CtClass[] primitiveTypes; - - static - { - primitiveTypes = new CtClass[9]; - - booleanType = new CtPrimitiveType("boolean", 'Z', "java.lang.Boolean", - "booleanValue", "()Z", Opcode.IRETURN, - Opcode.T_BOOLEAN, 1); - primitiveTypes[0] = booleanType; - - charType = new CtPrimitiveType("char", 'C', "java.lang.Character", - "charValue", "()C", Opcode.IRETURN, - Opcode.T_CHAR, 1); - primitiveTypes[1] = charType; - - byteType = new CtPrimitiveType("byte", 'B', "java.lang.Byte", - "byteValue", "()B", Opcode.IRETURN, - Opcode.T_BYTE, 1); - primitiveTypes[2] = byteType; - - shortType = new CtPrimitiveType("short", 'S', "java.lang.Short", - "shortValue", "()S", Opcode.IRETURN, - Opcode.T_SHORT, 1); - primitiveTypes[3] = shortType; - - intType = new CtPrimitiveType("int", 'I', "java.lang.Integer", - "intValue", "()I", Opcode.IRETURN, - Opcode.T_INT, 1); - primitiveTypes[4] = intType; - - longType = new CtPrimitiveType("long", 'J', "java.lang.Long", - "longValue", "()J", Opcode.LRETURN, - Opcode.T_LONG, 2); - primitiveTypes[5] = longType; - - floatType = new CtPrimitiveType("float", 'F', "java.lang.Float", - "floatValue", "()F", Opcode.FRETURN, - Opcode.T_FLOAT, 1); - primitiveTypes[6] = floatType; - - doubleType = new CtPrimitiveType("double", 'D', "java.lang.Double", - "doubleValue", "()D", Opcode.DRETURN, - Opcode.T_DOUBLE, 2); - primitiveTypes[7] = doubleType; - - voidType = new CtPrimitiveType("void", 'V', "java.lang.Void", - null, null, Opcode.RETURN, 0, 0); - primitiveTypes[8] = voidType; - } - - protected CtClass(String name) - { - qualifiedName = name; - } - - /** - * Converts the object to a string. - */ - public String toString() - { - StringBuffer buf = new StringBuffer(getClass().getName()); - buf.append("@"); - buf.append(Integer.toHexString(hashCode())); - buf.append("["); - extendToString(buf); - buf.append("]"); - return buf.toString(); - } - - /** - * Implemented in subclasses to add to the {@link #toString()} result. - * Subclasses should put a space before each token added to the buffer. - */ - protected void extendToString(StringBuffer buffer) - { - buffer.append(getName()); - } - - /** - * Returns a <code>ClassPool</code> for this class. - */ - public ClassPool getClassPool() - { - return null; - } - - /** - * Returns a class file for this class. - * <p/> - * <p>This method is not available if <code>isFrozen()</code> - * is true. - */ - public ClassFile getClassFile() - { - checkModify(); - return getClassFile2(); - } - - /** - * Undocumented method. Do not use; internal-use only. - */ - public ClassFile getClassFile2() - { - return null; - } - - /** - * Undocumented method. Do not use; internal-use only. - */ - public javassist.compiler.AccessorMaker getAccessorMaker() - { - return null; - } - - /** - * Returns the uniform resource locator (URL) of the class file. - */ - public URL getURL() throws NotFoundException - { - throw new NotFoundException(getName()); - } - - /** - * Returns true if the definition of the class has been modified. - */ - public boolean isModified() - { - return false; - } - - /** - * Returns true if the class has been loaded or written out - * and thus it cannot be modified any more. - * - * @see #defrost() - */ - public boolean isFrozen() - { - return true; - } - - void freeze() - { - } - - void checkModify() throws RuntimeException - { - if (isFrozen()) - throw new RuntimeException(getName() + " class is frozen"); - - // isModified() must return true after this method is invoked. - } - - /** - * Defrosts the class so that the class can be modified again. - * <p/> - * <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/> - * <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/> - * <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, - * or void. - */ - public boolean isPrimitive() - { - return false; - } - - /** - * Returns <code>true</code> if this object represents an array type. - */ - public boolean isArray() - { - return false; - } - - /** - * If this object represents an array, this method returns the component - * type of the array. Otherwise, it returns <code>null</code>. - */ - public CtClass getComponentType() throws NotFoundException - { - return null; - } - - /** - * Returns <code>true</code> if this class extends or implements - * <code>clazz</code>. It also returns <code>true</code> if - * this class is the same as <code>clazz</code>. - */ - public boolean subtypeOf(CtClass clazz) throws NotFoundException - { - return this == clazz || getName().equals(clazz.getName()); - } - - /** - * Obtains the fully-qualified name of the class. - */ - public String getName() - { - return qualifiedName; - } - - /** - * Obtains the not-qualified class name. - */ - public final String getSimpleName() - { - String qname = qualifiedName; - int index = qname.lastIndexOf('.'); - if (index < 0) - return qname; - else - return qname.substring(index + 1); - } - - /** - * Obtains the package name. It may be <code>null</code>. - */ - public final String getPackageName() - { - String qname = qualifiedName; - int index = qname.lastIndexOf('.'); - if (index < 0) - return null; - else - return qname.substring(0, index); - } - - /** - * Sets the class name - * - * @param name fully-qualified name - */ - public void setName(String name) - { - checkModify(); - if (name != null) - qualifiedName = name; - } - - /** - * Substitutes <code>newName</code> for all occurrences of a class - * name <code>oldName</code> in the class file. - * - * @param oldName replaced class name - * @param newName substituted class name - */ - public void replaceClassName(String oldName, String newName) - { - checkModify(); - } - - /** - * Changes class names appearing in the class file according to the - * given <code>map</code>. - * <p/> - * <p>All the class names appearing in the class file are tested - * with <code>map</code> to determine whether each class name is - * replaced or not. Thus this method can be used for collecting - * all the class names in the class file. To do that, first define - * a subclass of <code>ClassMap</code> so that <code>get()</code> - * records all the given parameters. Then, make an instance of - * that subclass as an empty hash-table. Finally, pass that instance - * to this method. After this method finishes, that instance would - * contain all the class names appearing in the class file. - * - * @param map the hashtable associating replaced class names - * with substituted names. - */ - public void replaceClassName(ClassMap map) - { - checkModify(); - } - - /** - * Returns a collection of the names of all the classes - * referenced in this class. - * That collection includes the name of this class. - * <p/> - * <p>This method may return <code>null</code>. - */ - public Collection getRefClasses() - { - ClassFile cf = getClassFile2(); - if (cf != null) - { - ClassMap cm = new ClassMap() - { - public void put(String oldname, String newname) - { - put0(oldname, newname); - } - - public Object get(Object jvmClassName) - { - String n = toJavaName((String) jvmClassName); - put0(n, n); - return null; - } - - public void fix(String name) - { - } - }; - cf.renameClass(cm); - return cm.values(); - } - else - return null; - } - - /** - * Determines whether this object represents a class or an interface. - * It returns <code>true</code> if this object represents an interface. - */ - public boolean isInterface() - { - return false; - } - - /** - * Returns the modifiers for this class, encoded in an integer. - * For decoding, use <code>javassist.Modifier</code>. - * - * @see Modifier - */ - public int getModifiers() - { - return 0; - } - - /** - * Sets the modifiers. - * - * @param mod modifiers encoded by - * <code>javassist.Modifier</code> - * @see Modifier - */ - public void setModifiers(int mod) - { - checkModify(); - } - - /** - * Determines whether the class directly or indirectly extends - * the given class. If this class extends a class A and - * the class A extends a class B, then subclassof(B) returns true. - * <p/> - * <p>This method returns true if the given class is identical to - * the class represented by this object. - */ - public boolean subclassOf(CtClass superclass) - { - return false; - } - - /** - * Obtains the class object representing the superclass of the - * class. - * It returns null if this object represents the - * <code>java.lang.Object</code> class and thus it does not have - * the super class. - * <p/> - * <p>If this object represents an interface, this method - * always returns the <code>java.lang.Object</code> class. - * To obtain the super interfaces - * extended by that interface, call <code>getInterfaces()</code>. - */ - public CtClass getSuperclass() throws NotFoundException - { - return null; - } - - /** - * Changes a super class unless this object represents an interface. - * The new super class must be compatible with the old one. - * <p/> - * <p>If this object represents an interface, this method is equivalent - * to <code>addInterface()</code>; it appends <code>clazz</code> to - * the list of the super interfaces extended by that interface. - * Note that an interface can extend multiple super interfaces. - */ - public void setSuperclass(CtClass clazz) throws CannotCompileException - { - checkModify(); - } - - /** - * Obtains the class objects representing the interfaces implemented - * by the class or, if this object represents an interface, the interfaces - * extended by that interface. - */ - public CtClass[] getInterfaces() throws NotFoundException - { - return new CtClass[0]; - } - - /** - * Sets implemented interfaces. If this object represents an interface, - * this method sets the interfaces extended by that interface. - * - * @param list a list of the <code>CtClass</code> objects - * representing interfaces, or - * <code>null</code> if the class implements - * no interfaces. - */ - public void setInterfaces(CtClass[] list) - { - checkModify(); - } - - /** - * Adds an interface. - * - * @param anInterface the added interface. - */ - public void addInterface(CtClass anInterface) - { - checkModify(); - } - - /** - * If this class is a member class or interface of another class, - * then the class enclosing this class is returned. - * - * @return null if this class is a top-level class. - */ - public CtClass getDeclaringClass() throws NotFoundException - { - return null; - } - - /** - * Makes a new nested class. Making a nested class modifies the - * data in this <code>CtClass</code>. - * <p/> - * <p>The current implementation only supports a static nested class. - * <code>isStatic</code> must be true. - * - * @param name the simple name of the nested class. - * @param isStatic true if the nested class is static. - */ - public CtClass makeNestedClass(String name, boolean isStatic) - { - throw new RuntimeException(getName() + " is not a class"); - } - - /** - * Returns an array containing <code>CtField</code> objects - * representing all the public fields of the class. - * That array includes public fields inherited from the - * superclasses. - */ - public CtField[] getFields() - { - return new CtField[0]; - } - - /** - * Returns the field with the specified name. The returned field - * may be a private field declared in a super class or interface. - */ - public CtField getField(String name) throws NotFoundException - { - throw new NotFoundException(name); - } - - /** - * @return null if the specified field is not found. - */ - CtField getField2(String name) - { - return null; - } - - /** - * Gets all the fields declared in the class. The inherited fields - * are not included. - * <p/> - * <p>Note: the result does not include inherited fields. - */ - public CtField[] getDeclaredFields() - { - return new CtField[0]; - } - - /** - * Retrieves the field with the specified name among the fields - * declared in the class. - * <p/> - * <p>Note: this method does not search the superclasses. - */ - public CtField getDeclaredField(String name) throws NotFoundException - { - throw new NotFoundException(name); - } - - /** - * Gets all the constructors and methods declared in the class. - */ - public CtBehavior[] getDeclaredBehaviors() - { - return new CtBehavior[0]; - } - - /** - * Returns an array containing <code>CtConstructor</code> objects - * representing all the public constructors of the class. - */ - public CtConstructor[] getConstructors() - { - return new CtConstructor[0]; - } - - /** - * Returns the constructor with the given signature, - * which is represented by a character string - * called method descriptor. - * For details of the method descriptor, see the JVM specification - * or <code>javassist.bytecode.Descriptor</code>. - * - * @param desc method descriptor - * @see javassist.bytecode.Descriptor - */ - public CtConstructor getConstructor(String desc) - throws NotFoundException - { - throw new NotFoundException("no such a constructor"); - } - - /** - * Gets all the constructors declared in the class. - * - * @see javassist.CtConstructor - */ - public CtConstructor[] getDeclaredConstructors() - { - return new CtConstructor[0]; - } - - /** - * Returns a constructor receiving the specified parameters. - * - * @param params parameter types. - */ - public CtConstructor getDeclaredConstructor(CtClass[] params) - throws NotFoundException - { - String desc = Descriptor.ofConstructor(params); - return getConstructor(desc); - } - - /** - * Gets the class initializer (static constructor) - * declared in the class. - * This method returns <code>null</code> if - * no class initializer is not declared. - * - * @see #makeClassInitializer() - * @see javassist.CtConstructor - */ - public CtConstructor getClassInitializer() - { - return null; - } - - /** - * Returns an array containing <code>CtMethod</code> objects - * representing all the public methods of the class. - * That array includes public methods inherited from the - * superclasses. - */ - public CtMethod[] getMethods() - { - return new CtMethod[0]; - } - - /** - * Returns the method with the given name and signature. - * The returned method may be declared in a super class. - * The method signature is represented by a character string - * called method descriptor, - * which is defined in the JVM specification. - * - * @param name method name - * @param desc method descriptor - * @see javassist.bytecode.Descriptor - */ - public CtMethod getMethod(String name, String desc) - throws NotFoundException - { - throw new NotFoundException(name); - } - - /** - * Gets all methods declared in the class. The inherited methods - * are not included. - * - * @see javassist.CtMethod - */ - public CtMethod[] getDeclaredMethods() - { - return new CtMethod[0]; - } - - /** - * Retrieves the method with the specified name and parameter types - * among the methods declared in the class. - * <p/> - * <p>Note: this method does not search the superclasses. - * - * @param name method name - * @param params parameter types - * @see javassist.CtMethod - */ - public CtMethod getDeclaredMethod(String name, CtClass[] params) - throws NotFoundException - { - throw new NotFoundException(name); - } - - /** - * Retrieves the method with the specified name among the methods - * declared in the class. If there are multiple methods with - * the specified name, then this method returns one of them. - * <p/> - * <p>Note: this method does not search the superclasses. - * - * @see javassist.CtMethod - */ - public CtMethod getDeclaredMethod(String name) throws NotFoundException - { - throw new NotFoundException(name); - } - - /** - * Makes an empty class initializer (static constructor). - * If the class already includes a class initializer, - * this method returns it. - * - * @see #getClassInitializer() - */ - public CtConstructor makeClassInitializer() - throws CannotCompileException - { - throw new CannotCompileException("not a class"); - } - - /** - * Adds a constructor. To add a class initializer (static constructor), - * call <code>makeClassInitializer()</code>. - * - * @see #makeClassInitializer() - */ - public void addConstructor(CtConstructor c) - throws CannotCompileException - { - checkModify(); - } - - /** - * Adds a method. - */ - public void addMethod(CtMethod m) throws CannotCompileException - { - checkModify(); - } - - /** - * Adds a field. - * <p/> - * <p>The <code>CtField</code> belonging to another - * <code>CtClass</code> cannot be directly added to this class. - * Only a field created for this class can be added. - * - * @see javassist.CtField#CtField(CtField,CtClass) - */ - public void addField(CtField f) throws CannotCompileException - { - addField(f, (CtField.Initializer) null); - } - - /** - * Adds a field with an initial value. - * <p/> - * <p>The <code>CtField</code> belonging to another - * <code>CtClass</code> cannot be directly added to this class. - * Only a field created for this class can be added. - * <p/> - * <p>The initial value is given as an expression written in Java. - * Any regular Java expression can be used for specifying the initial - * value. The followings are examples. - * <p/> - * <ul><pre> - * cc.addField(f, "0") // the initial value is 0. - * cc.addField(f, "i + 1") // i + 1. - * cc.addField(f, "new Point()"); // a Point object. - * </pre></ul> - * <p/> - * <p>Here, the type of variable <code>cc</code> is <code>CtClass</code>. - * The type of <code>f</code> is <code>CtField</code>. - * - * @param init an expression for the initial value. - * @see javassist.CtField.Initializer#byExpr(String) - * @see javassist.CtField#CtField(CtField,CtClass) - */ - public void addField(CtField f, String init) - throws CannotCompileException - { - checkModify(); - } - - /** - * Adds a field with an initial value. - * <p/> - * <p>The <code>CtField</code> belonging to another - * <code>CtClass</code> cannot be directly added to this class. - * Only a field created for this class can be added. - * <p/> - * <p>For example, - * <p/> - * <ul><pre> - * CtClass cc = ...; - * addField(new CtField(CtClass.intType, "i", cc), - * CtField.Initializer.constant(1)); - * </pre></ul> - * <p/> - * <p>This code adds an <code>int</code> field named "i". The - * initial value of this field is 1. - * - * @param init specifies the initial value of the field. - * @see javassist.CtField#CtField(CtField,CtClass) - */ - public void addField(CtField f, CtField.Initializer init) - throws CannotCompileException - { - checkModify(); - } - - /** - * Obtains an attribute with the given name. - * If that attribute is not found in the class file, this - * method returns null. - * <p/> - * <p>This is a convenient method mainly for obtaining - * a user-defined attribute. For dealing with attributes, see the - * <code>javassist.bytecode</code> package. For example, the following - * expression returns all the attributes of a class file. - * <p/> - * <ul><pre> - * getClassFile().getAttributes() - * </pre></ul> - * - * @param name attribute name - * @see javassist.bytecode.AttributeInfo - */ - public byte[] getAttribute(String name) - { - return null; - } - - /** - * Adds a named attribute. - * An arbitrary data (smaller than 64Kb) can be saved in the class - * file. Some attribute name are reserved by the JVM. - * The attributes with the non-reserved names are ignored when a - * class file is loaded into the JVM. - * If there is already an attribute with - * the same name, this method substitutes the new one for it. - * <p/> - * <p>This is a convenient method mainly for adding - * a user-defined attribute. For dealing with attributes, see the - * <code>javassist.bytecode</code> package. For example, the following - * expression adds an attribute of a class file. - * <p/> - * <ul><pre> - * getClassFile().addAttribute(info) - * </pre></ul> - * - * @param name attribute name - * @param data attribute value - * @see javassist.bytecode.AttributeInfo - */ - public void setAttribute(String name, byte[] data) - { - checkModify(); - } - - /** - * Applies the given converter to all methods and constructors - * declared in the class. This method calls <code>instrument()</code> - * on every <code>CtMethod</code> and <code>CtConstructor</code> object - * in the class. - * - * @param converter specifies how to modify. - */ - public void instrument(CodeConverter converter) - throws CannotCompileException - { - checkModify(); - } - - /** - * Modifies the bodies of all methods and constructors - * declared in the class. This method calls <code>instrument()</code> - * on every <code>CtMethod</code> and <code>CtConstructor</code> object - * in the class. - * - * @param editor specifies how to modify. - */ - public void instrument(ExprEditor editor) - throws CannotCompileException - { - checkModify(); - } - - /** - * Converts this class to a <code>java.lang.Class</code> object. - * Once this method is called, further modifications are not - * allowed any more. - * To load the class, this method uses the context class loader - * of the current thread. If the program is running on some application - * server, the context class loader might be inappropriate to load the - * class. - * <p/> - * <p>This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - * @see #toClass(java.lang.ClassLoader) - */ - public Class toClass() - throws CannotCompileException - { - return getClassPool().toClass(this, Thread.currentThread().getContextClassLoader()); - } - - /** - * Converts this class to a <code>java.lang.Class</code> object. - * Once this method is called, further modifications are not allowed - * any more. - * <p/> - * <p>The class file represented by this <code>CtClass</code> is - * loaded by the given class loader to construct a - * <code>java.lang.Class</code> object. Since a private method - * on the class loader is invoked through the reflection API, - * the caller must have permissions to do that. - * <p/> - * <p>This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - * @param loader the class loader used to load this class. - */ - public Class toClass(ClassLoader loader) - throws CannotCompileException - { - return getClassPool().toClass(this, loader); - } - - /** - * 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/> - * <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. - */ - public void detach() - { - ClassPool cp = getClassPool(); - CtClass obj = cp.removeCached(getName()); - if (obj != this) - cp.cacheCtClass(getName(), obj); - } - - /** - * Converts this class to a class file. - * Once this method is called, further modifications are not - * possible any more. - * - * @return the contents of the class file. - */ - public byte[] toBytecode() - throws IOException, CannotCompileException - { - ByteArrayOutputStream barray = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(barray); - try - { - toBytecode(out); - } - finally - { - out.close(); - } - - return barray.toByteArray(); - } - - /** - * Writes a class file represented by this <code>CtClass</code> - * object in the current directory. - * Once this method is called, further modifications are not - * possible any more. - */ - public void writeFile() - throws NotFoundException, IOException, CannotCompileException - { - writeFile("."); - } - - /** - * Writes a class file represented by this <code>CtClass</code> - * object on a local disk. - * Once this method is called, further modifications are not - * possible any more. - * - * @param directoryName it must end without a directory separator. - */ - public void writeFile(String directoryName) - throws NotFoundException, CannotCompileException, IOException - { - String classname = getName(); - String filename = directoryName + File.separatorChar - + classname.replace('.', File.separatorChar) + ".class"; - int pos = filename.lastIndexOf(File.separatorChar); - if (pos > 0) - { - String dir = filename.substring(0, pos); - if (!dir.equals(".")) - new File(dir).mkdirs(); - } - - DataOutputStream out - = new DataOutputStream(new BufferedOutputStream(new DelayedFileOutputStream(filename))); - try - { - toBytecode(out); - } - finally - { - out.close(); - } - } - - static class DelayedFileOutputStream extends OutputStream - { - private FileOutputStream file; - private String filename; - - DelayedFileOutputStream(String name) - { - file = null; - filename = name; - } - - private void init() throws IOException - { - if (file == null) - file = new FileOutputStream(filename); - } - - public void write(int b) throws IOException - { - init(); - file.write(b); - } - - public void write(byte[] b) throws IOException - { - init(); - file.write(b); - } - - public void write(byte[] b, int off, int len) throws IOException - { - init(); - file.write(b, off, len); - - } - - public void flush() throws IOException - { - init(); - file.flush(); - } - - public void close() throws IOException - { - init(); - file.close(); - } - } - - /** - * Converts this class to a class file. - * Once this method is called, further modifications are not - * possible any more. - * <p/> - * <p>This method dose not close the output stream in the end. - * - * @param out the output stream that a class file is written to. - */ - public void toBytecode(DataOutputStream out) - throws CannotCompileException, IOException - { - throw new CannotCompileException("not a class"); - } +public abstract class CtClass { + protected String qualifiedName; + + /** + * The version number of this release. + */ + public static final String version = "3.0 RC1"; + + /** + * Prints the version number and the copyright notice. + * + * <p>The following command invokes this method: + * + * <ul><pre>java -jar javassist.jar</pre></ul> + */ + public static void main(String[] args) { + System.out.println("Javassist version " + CtClass.version); + System.out.println("Copyright (C) 1999-2004 Shigeru Chiba." + + " All Rights Reserved."); + } + + static final String javaLangObject = "java.lang.Object"; + + /** + * The <code>CtClass</code> object representing + * the <code>boolean</code> type. + */ + public static CtClass booleanType; + + /** + * The <code>CtClass</code> object representing + * the <code>char</code> type. + */ + public static CtClass charType; + + /** + * The <code>CtClass</code> object representing + * the <code>byte</code> type. + */ + public static CtClass byteType; + + /** + * The <code>CtClass</code> object representing + * the <code>short</code> type. + */ + public static CtClass shortType; + + /** + * The <code>CtClass</code> object representing + * the <code>int</code> type. + */ + public static CtClass intType; + + /** + * The <code>CtClass</code> object representing + * the <code>long</code> type. + */ + public static CtClass longType; + + /** + * The <code>CtClass</code> object representing + * the <code>float</code> type. + */ + public static CtClass floatType; + + /** + * The <code>CtClass</code> object representing + * the <code>double</code> type. + */ + public static CtClass doubleType; + + /** + * The <code>CtClass</code> object representing + * the <code>void</code> type. + */ + public static CtClass voidType; + + static CtClass[] primitiveTypes; + + static { + primitiveTypes = new CtClass[9]; + + booleanType = + new CtPrimitiveType("boolean", 'Z', "java.lang.Boolean", + "booleanValue", "()Z", Opcode.IRETURN, + Opcode.T_BOOLEAN, 1); + primitiveTypes[0] = booleanType; + + charType = new CtPrimitiveType("char", 'C', "java.lang.Character", + "charValue", "()C", Opcode.IRETURN, + Opcode.T_CHAR, 1); + primitiveTypes[1] = charType; + + byteType = new CtPrimitiveType("byte", 'B', "java.lang.Byte", + "byteValue", "()B", Opcode.IRETURN, + Opcode.T_BYTE, 1); + primitiveTypes[2] = byteType; + + shortType = new CtPrimitiveType("short", 'S', "java.lang.Short", + "shortValue", "()S", Opcode.IRETURN, + Opcode.T_SHORT, 1); + primitiveTypes[3] = shortType; + + intType = new CtPrimitiveType("int", 'I', "java.lang.Integer", + "intValue", "()I", Opcode.IRETURN, + Opcode.T_INT, 1); + primitiveTypes[4] = intType; + + longType = new CtPrimitiveType("long", 'J', "java.lang.Long", + "longValue", "()J", Opcode.LRETURN, + Opcode.T_LONG, 2); + primitiveTypes[5] = longType; + + floatType = new CtPrimitiveType("float", 'F', "java.lang.Float", + "floatValue", "()F", Opcode.FRETURN, + Opcode.T_FLOAT, 1); + primitiveTypes[6] = floatType; + + doubleType = new CtPrimitiveType("double", 'D', "java.lang.Double", + "doubleValue", "()D", Opcode.DRETURN, + Opcode.T_DOUBLE, 2); + primitiveTypes[7] = doubleType; + + voidType = new CtPrimitiveType("void", 'V', "java.lang.Void", + null, null, Opcode.RETURN, 0, 0); + primitiveTypes[8] = voidType; + } + + protected CtClass(String name) { + qualifiedName = name; + } + + /** + * Converts the object to a string. + */ + public String toString() { + StringBuffer buf = new StringBuffer(getClass().getName()); + buf.append("@"); + buf.append(Integer.toHexString(hashCode())); + buf.append("["); + extendToString(buf); + buf.append("]"); + return buf.toString(); + } + + /** + * Implemented in subclasses to add to the {@link #toString()} result. + * Subclasses should put a space before each token added to the buffer. + */ + protected void extendToString(StringBuffer buffer) { + buffer.append(getName()); + } + + /** + * Returns a <code>ClassPool</code> for this class. + */ + public ClassPool getClassPool() { return null; } + + /** + * Returns a class file for this class. + * + * <p>This method is not available if <code>isFrozen()</code> + * is true. + */ + public ClassFile getClassFile() { + checkModify(); + return getClassFile2(); + } + + /** + * Undocumented method. Do not use; internal-use only. + */ + public ClassFile getClassFile2() { return null; } + + /** + * Undocumented method. Do not use; internal-use only. + */ + public javassist.compiler.AccessorMaker getAccessorMaker() { + return null; + } + + /** + * Returns the uniform resource locator (URL) of the class file. + */ + public URL getURL() throws NotFoundException { + throw new NotFoundException(getName()); + } + + /** + * Returns true if the definition of the class has been modified. + */ + public boolean isModified() { return false; } + + /** + * Returns true if the class has been loaded or written out + * and thus it cannot be modified any more. + * + * @see #defrost() + */ + public boolean isFrozen() { return true; } + + void freeze() {} + + void checkModify() throws RuntimeException { + if (isFrozen()) + throw new RuntimeException(getName() + " class is frozen"); + + // isModified() must return true after this method is invoked. + } + + /** + * Defrosts the class so that the class can be modified again. + * + * <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, + * or void. + */ + public boolean isPrimitive() { return false; } + + /** + * Returns <code>true</code> if this object represents an array type. + */ + public boolean isArray() { + return false; + } + + /** + * If this object represents an array, this method returns the component + * type of the array. Otherwise, it returns <code>null</code>. + */ + public CtClass getComponentType() throws NotFoundException { + return null; + } + + /** + * Returns <code>true</code> if this class extends or implements + * <code>clazz</code>. It also returns <code>true</code> if + * this class is the same as <code>clazz</code>. + */ + public boolean subtypeOf(CtClass clazz) throws NotFoundException { + return this == clazz || getName().equals(clazz.getName()); + } + + /** + * Obtains the fully-qualified name of the class. + */ + public String getName() { return qualifiedName; } + + /** + * Obtains the not-qualified class name. + */ + public final String getSimpleName() { + String qname = qualifiedName; + int index = qname.lastIndexOf('.'); + if (index < 0) + return qname; + else + return qname.substring(index + 1); + } + + /** + * Obtains the package name. It may be <code>null</code>. + */ + public final String getPackageName() { + String qname = qualifiedName; + int index = qname.lastIndexOf('.'); + if (index < 0) + return null; + else + return qname.substring(0, index); + } + + /** + * Sets the class name + * + * @param name fully-qualified name + */ + public void setName(String name) { + checkModify(); + if (name != null) + qualifiedName = name; + } + + /** + * Substitutes <code>newName</code> for all occurrences of a class + * name <code>oldName</code> in the class file. + * + * @param oldName replaced class name + * @param newName substituted class name + */ + public void replaceClassName(String oldName, String newName) { + checkModify(); + } + + /** + * Changes class names appearing in the class file according to the + * given <code>map</code>. + * + * <p>All the class names appearing in the class file are tested + * with <code>map</code> to determine whether each class name is + * replaced or not. Thus this method can be used for collecting + * all the class names in the class file. To do that, first define + * a subclass of <code>ClassMap</code> so that <code>get()</code> + * records all the given parameters. Then, make an instance of + * that subclass as an empty hash-table. Finally, pass that instance + * to this method. After this method finishes, that instance would + * contain all the class names appearing in the class file. + * + * @param map the hashtable associating replaced class names + * with substituted names. + */ + public void replaceClassName(ClassMap map) { + checkModify(); + } + + /** + * Returns a collection of the names of all the classes + * referenced in this class. + * That collection includes the name of this class. + * + * <p>This method may return <code>null</code>. + */ + public Collection getRefClasses() { + ClassFile cf = getClassFile2(); + if (cf != null) { + ClassMap cm = new ClassMap() { + public void put(String oldname, String newname) { + put0(oldname, newname); + } + + public Object get(Object jvmClassName) { + String n = toJavaName((String)jvmClassName); + put0(n, n); + return null; + } + + public void fix(String name) {} + }; + cf.renameClass(cm); + return cm.values(); + } + else + return null; + } + + /** + * Determines whether this object represents a class or an interface. + * It returns <code>true</code> if this object represents an interface. + */ + public boolean isInterface() { + return false; + } + + /** + * Returns the modifiers for this class, encoded in an integer. + * For decoding, use <code>javassist.Modifier</code>. + * + * @see Modifier + */ + public int getModifiers() { + return 0; + } + + /** + * Sets the modifiers. + * + * @param mod modifiers encoded by + * <code>javassist.Modifier</code> + * @see Modifier + */ + public void setModifiers(int mod) { + checkModify(); + } + + /** + * Determines whether the class directly or indirectly extends + * the given class. If this class extends a class A and + * the class A extends a class B, then subclassof(B) returns true. + * + * <p>This method returns true if the given class is identical to + * the class represented by this object. + */ + public boolean subclassOf(CtClass superclass) { + return false; + } + + /** + * Obtains the class object representing the superclass of the + * class. + * It returns null if this object represents the + * <code>java.lang.Object</code> class and thus it does not have + * the super class. + * + * <p>If this object represents an interface, this method + * always returns the <code>java.lang.Object</code> class. + * To obtain the super interfaces + * extended by that interface, call <code>getInterfaces()</code>. + */ + public CtClass getSuperclass() throws NotFoundException { + return null; + } + + /** + * Changes a super class unless this object represents an interface. + * The new super class must be compatible with the old one. + * + * <p>If this object represents an interface, this method is equivalent + * to <code>addInterface()</code>; it appends <code>clazz</code> to + * the list of the super interfaces extended by that interface. + * Note that an interface can extend multiple super interfaces. + */ + public void setSuperclass(CtClass clazz) throws CannotCompileException { + checkModify(); + } + + /** + * Obtains the class objects representing the interfaces implemented + * by the class or, if this object represents an interface, the interfaces + * extended by that interface. + */ + public CtClass[] getInterfaces() throws NotFoundException { + return new CtClass[0]; + } + + /** + * Sets implemented interfaces. If this object represents an interface, + * this method sets the interfaces extended by that interface. + * + * @param list a list of the <code>CtClass</code> objects + * representing interfaces, or + * <code>null</code> if the class implements + * no interfaces. + */ + public void setInterfaces(CtClass[] list) { + checkModify(); + } + + /** + * Adds an interface. + * + * @param anInterface the added interface. + */ + public void addInterface(CtClass anInterface) { + checkModify(); + } + + /** + * If this class is a member class or interface of another class, + * then the class enclosing this class is returned. + * + * @return null if this class is a top-level class. + */ + public CtClass getDeclaringClass() throws NotFoundException { + return null; + } + + /** + * Makes a new nested class. Making a nested class modifies the + * data in this <code>CtClass</code>. + * + * <p>The current implementation only supports a static nested class. + * <code>isStatic</code> must be true. + * + * @param name the simple name of the nested class. + * @param isStatic true if the nested class is static. + */ + public CtClass makeNestedClass(String name, boolean isStatic) { + throw new RuntimeException(getName() + " is not a class"); + } + + /** + * Returns an array containing <code>CtField</code> objects + * representing all the public fields of the class. + * That array includes public fields inherited from the + * superclasses. + */ + public CtField[] getFields() { return new CtField[0]; } + + /** + * Returns the field with the specified name. The returned field + * may be a private field declared in a super class or interface. + */ + public CtField getField(String name) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * @return null if the specified field is not found. + */ + CtField getField2(String name) { return null; } + + /** + * Gets all the fields declared in the class. The inherited fields + * are not included. + * + * <p>Note: the result does not include inherited fields. + */ + public CtField[] getDeclaredFields() { return new CtField[0]; } + + /** + * Retrieves the field with the specified name among the fields + * declared in the class. + * + * <p>Note: this method does not search the superclasses. + */ + public CtField getDeclaredField(String name) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * Gets all the constructors and methods declared in the class. + */ + public CtBehavior[] getDeclaredBehaviors() { + return new CtBehavior[0]; + } + + /** + * Returns an array containing <code>CtConstructor</code> objects + * representing all the public constructors of the class. + */ + public CtConstructor[] getConstructors() { + return new CtConstructor[0]; + } + + /** + * Returns the constructor with the given signature, + * which is represented by a character string + * called method descriptor. + * For details of the method descriptor, see the JVM specification + * or <code>javassist.bytecode.Descriptor</code>. + * + * @param desc method descriptor + * @see javassist.bytecode.Descriptor + */ + public CtConstructor getConstructor(String desc) + throws NotFoundException + { + throw new NotFoundException("no such a constructor"); + } + + /** + * Gets all the constructors declared in the class. + * + * @see javassist.CtConstructor + */ + public CtConstructor[] getDeclaredConstructors() { + return new CtConstructor[0]; + } + + /** + * Returns a constructor receiving the specified parameters. + * + * @param params parameter types. + */ + public CtConstructor getDeclaredConstructor(CtClass[] params) + throws NotFoundException + { + String desc = Descriptor.ofConstructor(params); + return getConstructor(desc); + } + + /** + * Gets the class initializer (static constructor) + * declared in the class. + * This method returns <code>null</code> if + * no class initializer is not declared. + * + * @see #makeClassInitializer() + * @see javassist.CtConstructor + */ + public CtConstructor getClassInitializer() { + return null; + } + + /** + * Returns an array containing <code>CtMethod</code> objects + * representing all the public methods of the class. + * That array includes public methods inherited from the + * superclasses. + */ + public CtMethod[] getMethods() { + return new CtMethod[0]; + } + + /** + * Returns the method with the given name and signature. + * The returned method may be declared in a super class. + * The method signature is represented by a character string + * called method descriptor, + * which is defined in the JVM specification. + * + * @param name method name + * @param desc method descriptor + * @see javassist.bytecode.Descriptor + */ + public CtMethod getMethod(String name, String desc) + throws NotFoundException + { + throw new NotFoundException(name); + } + + /** + * Gets all methods declared in the class. The inherited methods + * are not included. + * + * @see javassist.CtMethod + */ + public CtMethod[] getDeclaredMethods() { + return new CtMethod[0]; + } + + /** + * Retrieves the method with the specified name and parameter types + * among the methods declared in the class. + * + * <p>Note: this method does not search the superclasses. + * + * @param name method name + * @param params parameter types + * @see javassist.CtMethod + */ + public CtMethod getDeclaredMethod(String name, CtClass[] params) + throws NotFoundException + { + throw new NotFoundException(name); + } + + /** + * Retrieves the method with the specified name among the methods + * declared in the class. If there are multiple methods with + * the specified name, then this method returns one of them. + * + * <p>Note: this method does not search the superclasses. + * + * @see javassist.CtMethod + */ + public CtMethod getDeclaredMethod(String name) throws NotFoundException { + throw new NotFoundException(name); + } + + /** + * Makes an empty class initializer (static constructor). + * If the class already includes a class initializer, + * this method returns it. + * + * @see #getClassInitializer() + */ + public CtConstructor makeClassInitializer() + throws CannotCompileException + { + throw new CannotCompileException("not a class"); + } + + /** + * Adds a constructor. To add a class initializer (static constructor), + * call <code>makeClassInitializer()</code>. + * + * @see #makeClassInitializer() + */ + public void addConstructor(CtConstructor c) + throws CannotCompileException + { + checkModify(); + } + + /** + * Adds a method. + */ + public void addMethod(CtMethod m) throws CannotCompileException { + checkModify(); + } + + /** + * Adds a field. + * + * <p>The <code>CtField</code> belonging to another + * <code>CtClass</code> cannot be directly added to this class. + * Only a field created for this class can be added. + * + * @see javassist.CtField#CtField(CtField,CtClass) + */ + public void addField(CtField f) throws CannotCompileException { + addField(f, (CtField.Initializer)null); + } + + /** + * Adds a field with an initial value. + * + * <p>The <code>CtField</code> belonging to another + * <code>CtClass</code> cannot be directly added to this class. + * Only a field created for this class can be added. + * + * <p>The initial value is given as an expression written in Java. + * Any regular Java expression can be used for specifying the initial + * value. The followings are examples. + * + * <ul><pre> + * cc.addField(f, "0") // the initial value is 0. + * cc.addField(f, "i + 1") // i + 1. + * cc.addField(f, "new Point()"); // a Point object. + * </pre></ul> + * + * <p>Here, the type of variable <code>cc</code> is <code>CtClass</code>. + * The type of <code>f</code> is <code>CtField</code>. + * + * @param init an expression for the initial value. + * + * @see javassist.CtField.Initializer#byExpr(String) + * @see javassist.CtField#CtField(CtField,CtClass) + */ + public void addField(CtField f, String init) + throws CannotCompileException + { + checkModify(); + } + + /** + * Adds a field with an initial value. + * + * <p>The <code>CtField</code> belonging to another + * <code>CtClass</code> cannot be directly added to this class. + * Only a field created for this class can be added. + * + * <p>For example, + * + * <ul><pre> + * CtClass cc = ...; + * addField(new CtField(CtClass.intType, "i", cc), + * CtField.Initializer.constant(1)); + * </pre></ul> + * + * <p>This code adds an <code>int</code> field named "i". The + * initial value of this field is 1. + * + * @param init specifies the initial value of the field. + * + * @see javassist.CtField#CtField(CtField,CtClass) + */ + public void addField(CtField f, CtField.Initializer init) + throws CannotCompileException + { + checkModify(); + } + + /** + * Obtains an attribute with the given name. + * If that attribute is not found in the class file, this + * method returns null. + * + * <p>This is a convenient method mainly for obtaining + * a user-defined attribute. For dealing with attributes, see the + * <code>javassist.bytecode</code> package. For example, the following + * expression returns all the attributes of a class file. + * + * <ul><pre> + * getClassFile().getAttributes() + * </pre></ul> + * + * @param name attribute name + * @see javassist.bytecode.AttributeInfo + */ + public byte[] getAttribute(String name) { + return null; + } + + /** + * Adds a named attribute. + * An arbitrary data (smaller than 64Kb) can be saved in the class + * file. Some attribute name are reserved by the JVM. + * The attributes with the non-reserved names are ignored when a + * class file is loaded into the JVM. + * If there is already an attribute with + * the same name, this method substitutes the new one for it. + * + * <p>This is a convenient method mainly for adding + * a user-defined attribute. For dealing with attributes, see the + * <code>javassist.bytecode</code> package. For example, the following + * expression adds an attribute of a class file. + * + * <ul><pre> + * getClassFile().addAttribute(info) + * </pre></ul> + * + * @param name attribute name + * @param data attribute value + * @see javassist.bytecode.AttributeInfo + */ + public void setAttribute(String name, byte[] data) { + checkModify(); + } + + /** + * Applies the given converter to all methods and constructors + * declared in the class. This method calls <code>instrument()</code> + * on every <code>CtMethod</code> and <code>CtConstructor</code> object + * in the class. + * + * @param converter specifies how to modify. + */ + public void instrument(CodeConverter converter) + throws CannotCompileException + { + checkModify(); + } + + /** + * Modifies the bodies of all methods and constructors + * declared in the class. This method calls <code>instrument()</code> + * on every <code>CtMethod</code> and <code>CtConstructor</code> object + * in the class. + * + * @param editor specifies how to modify. + */ + public void instrument(ExprEditor editor) + throws CannotCompileException + { + checkModify(); + } + + /** + * Converts this class to a <code>java.lang.Class</code> object. + * Once this method is called, further modifications are not + * allowed any more. + * To load the class, this method uses the context class loader + * of the current thread. If the program is running on some application + * server, the context class loader might be inappropriate to load the + * class. + * + * <p>This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + * <p>Note: this method calls <code>toClass()</code> + * in <code>ClassPool</code>. + * + * @see #toClass(java.lang.ClassLoader) + * @see ClassPool#toClass(java.lang.ClassLoader) + */ + public Class toClass() throws CannotCompileException { + return getClassPool().toClass(this); + } + + /** + * Converts this class to a <code>java.lang.Class</code> object. + * Once this method is called, further modifications are not allowed + * any more. + * + * <p>The class file represented by this <code>CtClass</code> is + * loaded by the given class loader to construct a + * <code>java.lang.Class</code> object. Since a private method + * on the class loader is invoked through the reflection API, + * the caller must have permissions to do that. + * + * <p>This method is provided for convenience. If you need more + * complex functionality, you should write your own class loader. + * + * <p>Note: this method calls <code>toClass()</code> + * in <code>ClassPool</code>. + * + * @param loader the class loader used to load this class. + * @see ClassPool#toClass(CtClass,java.lang.ClassLoader) + */ + public Class toClass(ClassLoader loader) + throws CannotCompileException + { + return getClassPool().toClass(this, loader); + } + + /** + * 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 <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. + */ + public void detach() { + ClassPool cp = getClassPool(); + CtClass obj = cp.removeCached(getName()); + if (obj != this) + cp.cacheCtClass(getName(), obj); + } + + /** + * Converts this class to a class file. + * Once this method is called, further modifications are not + * possible any more. + * + * @return the contents of the class file. + */ + public byte[] toBytecode() throws IOException, CannotCompileException { + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(barray); + try { + toBytecode(out); + } + finally { + out.close(); + } + + return barray.toByteArray(); + } + + /** + * Writes a class file represented by this <code>CtClass</code> + * object in the current directory. + * Once this method is called, further modifications are not + * possible any more. + */ + public void writeFile() + throws NotFoundException, IOException, CannotCompileException + { + writeFile("."); + } + + /** + * Writes a class file represented by this <code>CtClass</code> + * object on a local disk. + * Once this method is called, further modifications are not + * possible any more. + * + * @param directoryName it must end without a directory separator. + */ + public void writeFile(String directoryName) + throws NotFoundException, CannotCompileException, IOException + { + String classname = getName(); + String filename = directoryName + File.separatorChar + + classname.replace('.', File.separatorChar) + ".class"; + int pos = filename.lastIndexOf(File.separatorChar); + if (pos > 0) { + String dir = filename.substring(0, pos); + if (!dir.equals(".")) + new File(dir).mkdirs(); + } + + DataOutputStream out + = new DataOutputStream(new BufferedOutputStream( + new DelayedFileOutputStream(filename))); + try { + toBytecode(out); + } + finally { + out.close(); + } + } + + static class DelayedFileOutputStream extends OutputStream { + private FileOutputStream file; + private String filename; + + DelayedFileOutputStream(String name) { + file = null; + filename = name; + } + + private void init() throws IOException { + if (file == null) + file = new FileOutputStream(filename); + } + + public void write(int b) throws IOException { + init(); + file.write(b); + } + + public void write(byte[] b) throws IOException { + init(); + file.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + init(); + file.write(b, off, len); + + } + + public void flush() throws IOException { + init(); + file.flush(); + } + + public void close() throws IOException { + init(); + file.close(); + } + } + + /** + * Converts this class to a class file. + * Once this method is called, further modifications are not + * possible any more. + * + * <p>This method dose not close the output stream in the end. + * + * @param out the output stream that a class file is written to. + */ + public void toBytecode(DataOutputStream out) + throws CannotCompileException, IOException + { + throw new CannotCompileException("not a class"); + } } diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 177f772d..5b249a42 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -47,1235 +47,1067 @@ import java.util.List; /** * Class types. */ -class CtClassType extends CtClass -{ - ClassPool classPool; - boolean wasChanged; - private boolean wasFrozen; - boolean wasPruned; - ClassFile classfile; - - private CtField fieldsCache; - private CtConstructor constructorsCache; - private CtConstructor classInitializerCache; - private CtMethod methodsCache; - - private AccessorMaker accessors; - - private FieldInitLink fieldInitializers; - private Hashtable hiddenMethods; // must be synchronous - private int uniqueNumberSeed; - - private boolean doPruning = false; - 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 = wasPruned = false; - classfile = null; - accessors = null; - fieldInitializers = null; - hiddenMethods = null; - uniqueNumberSeed = 0; - eraseCache(); - getCounter = 0; - } - - CtClassType(InputStream ins, ClassPool cp) throws IOException - { - this((String) null, cp); - classfile = new ClassFile(new DataInputStream(ins)); - qualifiedName = classfile.getName(); - } - - protected void extendToString(StringBuffer buffer) - { - if (wasChanged) - buffer.append("changed "); - - if (wasFrozen) - buffer.append("frozen "); - - if (wasPruned) - buffer.append("pruned "); - - buffer.append(Modifier.toString(getModifiers())); - buffer.append(" class "); - buffer.append(getName()); - - try - { - CtClass ext = getSuperclass(); - if (ext != null) - { - String name = ext.getName(); - if (!name.equals("java.lang.Object")) - buffer.append(" extends " + ext.getName()); - } - } - catch (NotFoundException e) - { - buffer.append(" extends ??"); - } - - try - { - CtClass[] intf = getInterfaces(); - if (intf.length > 0) - buffer.append(" implements "); - - for (int i = 0; i < intf.length; ++i) - { - buffer.append(intf[i].getName()); - buffer.append(", "); - } - } - catch (NotFoundException e) - { - buffer.append(" extends ??"); - } - - CtField field = getFieldsCache(); - buffer.append(" fields="); - while (field != null) - { - buffer.append(field); - buffer.append(", "); - field = field.next; - } - - CtConstructor c = getConstructorsCache(); - buffer.append(" constructors="); - while (c != null) - { - buffer.append(c); - buffer.append(", "); - c = c.next; - } - - CtMethod m = getMethodsCache(); - buffer.append(" methods="); - while (m != null) - { - buffer.append(m); - 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); - - return accessors; - } - - public ClassFile getClassFile2() - { - if (classfile != null) - return classfile; - - if (readCounter++ > READ_THRESHOLD) - { - doCompaction(); - readCounter = 0; - } - - InputStream fin = null; - try - { - fin = classPool.openClassfile(getName()); - if (fin == null) - throw new NotFoundException(getName()); - - fin = new BufferedInputStream(fin); - classfile = new ClassFile(new DataInputStream(fin)); - return classfile; - } - catch (NotFoundException e) - { - throw new RuntimeException(e.toString()); - } - catch (IOException e) - { - throw new RuntimeException(e.toString()); - } - finally - { - if (fin != null) - try - { - fin.close(); +class CtClassType extends CtClass { + ClassPool classPool; + boolean wasChanged; + private boolean wasFrozen; + boolean wasPruned; + ClassFile classfile; + + private CtField fieldsCache; + private CtConstructor constructorsCache; + private CtConstructor classInitializerCache; + private CtMethod methodsCache; + + private AccessorMaker accessors; + + private FieldInitLink fieldInitializers; + private Hashtable hiddenMethods; // must be synchronous + private int uniqueNumberSeed; + + private boolean doPruning = false; + 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 = wasPruned = false; + classfile = null; + accessors = null; + fieldInitializers = null; + hiddenMethods = null; + uniqueNumberSeed = 0; + eraseCache(); + getCounter = 0; + } + + CtClassType(InputStream ins, ClassPool cp) throws IOException { + this((String)null, cp); + classfile = new ClassFile(new DataInputStream(ins)); + qualifiedName = classfile.getName(); + } + + protected void extendToString(StringBuffer buffer) { + if (wasChanged) + buffer.append("changed "); + + if (wasFrozen) + buffer.append("frozen "); + + if (wasPruned) + buffer.append("pruned "); + + buffer.append(Modifier.toString(getModifiers())); + buffer.append(" class "); + buffer.append(getName()); + + try { + CtClass ext = getSuperclass(); + if (ext != null) { + String name = ext.getName(); + if (!name.equals("java.lang.Object")) + buffer.append(" extends " + ext.getName()); } - catch (IOException e) - { + } + catch (NotFoundException e) { + buffer.append(" extends ??"); + } + + try { + CtClass[] intf = getInterfaces(); + if (intf.length > 0) + buffer.append(" implements "); + + for (int i = 0; i < intf.length; ++i) { + buffer.append(intf[i].getName()); + buffer.append(", "); } - } - } - - /* 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; + } + catch (NotFoundException e) { + buffer.append(" extends ??"); + } + + CtField field = getFieldsCache(); + buffer.append(" fields="); + while (field != null) { + buffer.append(field); + buffer.append(", "); + field = field.next; + } + + CtConstructor c = getConstructorsCache(); + buffer.append(" constructors="); + while (c != null) { + buffer.append(c); + buffer.append(", "); + c = c.next; + } + + CtMethod m = getMethodsCache(); + buffer.append(" methods="); + while (m != null) { + buffer.append(m); + 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); + + return accessors; + } + + public ClassFile getClassFile2() { + if (classfile != null) + return classfile; + + if (readCounter++ > READ_THRESHOLD) { + doCompaction(); + readCounter = 0; + } + + InputStream fin = null; + try { + fin = classPool.openClassfile(getName()); + if (fin == null) + throw new NotFoundException(getName()); + + fin = new BufferedInputStream(fin); + classfile = new ClassFile(new DataInputStream(fin)); + return classfile; + } + catch (NotFoundException e) { + throw new RuntimeException(e.toString()); + } + catch (IOException e) { + throw new RuntimeException(e.toString()); + } + finally { + if (fin != null) + try { + fin.close(); + } + catch (IOException e) {} + } + } + + /* 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; } + + public URL getURL() throws NotFoundException { + URL url = classPool.find(getName()); + if (url == null) + throw new NotFoundException(getName()); + else + return url; + } + + public boolean isModified() { return wasChanged; } - cct.getCounter = 0; - } - } - } - - public ClassPool getClassPool() - { - return classPool; - } - - void setClassPool(ClassPool cp) - { - classPool = cp; - } - - public URL getURL() throws NotFoundException - { - URL url = classPool.find(getName()); - if (url == null) - throw new NotFoundException(getName()); - else - return url; - } - - public boolean isModified() - { - return wasChanged; - } - - public boolean isFrozen() - { - return wasFrozen; - } - - void freeze() - { - wasFrozen = true; - } - - void checkModify() throws RuntimeException - { - super.checkModify(); - wasChanged = true; - } - - public void defrost() - { - checkPruned("defrost"); - wasFrozen = false; - } - - public boolean subtypeOf(CtClass clazz) throws NotFoundException - { - int i; - String cname = clazz.getName(); - if (this == clazz || getName().equals(cname)) - return true; - - ClassFile file = getClassFile2(); - String supername = file.getSuperclass(); - if (supername != null && supername.equals(cname)) - return true; - - String[] ifs = file.getInterfaces(); - int num = ifs.length; - for (i = 0; i < num; ++i) - if (ifs[i].equals(cname)) + public boolean isFrozen() { return wasFrozen; } + + void freeze() { wasFrozen = true; } + + void checkModify() throws RuntimeException { + super.checkModify(); + wasChanged = true; + } + + public void defrost() { + checkPruned("defrost"); + wasFrozen = false; + } + + public boolean subtypeOf(CtClass clazz) throws NotFoundException { + int i; + String cname = clazz.getName(); + if (this == clazz || getName().equals(cname)) + return true; + + ClassFile file = getClassFile2(); + String supername = file.getSuperclass(); + if (supername != null && supername.equals(cname)) return true; - if (supername != null && classPool.get(supername).subtypeOf(clazz)) - return true; + String[] ifs = file.getInterfaces(); + int num = ifs.length; + for (i = 0; i < num; ++i) + if (ifs[i].equals(cname)) + return true; - for (i = 0; i < num; ++i) - if (classPool.get(ifs[i]).subtypeOf(clazz)) + if (supername != null && classPool.get(supername).subtypeOf(clazz)) return true; - return false; - } - - public void setName(String name) throws RuntimeException - { - String oldname = getName(); - if (name.equals(oldname)) - return; - - // check this in advance although classNameChanged() below does. - classPool.checkNotFrozen(name); - ClassFile cf = getClassFile2(); - super.setName(name); - cf.setName(name); - eraseCache(); - classPool.classNameChanged(oldname, this); - } - - public void replaceClassName(ClassMap classnames) - throws RuntimeException - { - String oldClassName = getName(); - String newClassName - = (String) classnames.get(Descriptor.toJvmName(oldClassName)); - if (newClassName != null) - { - newClassName = Descriptor.toJavaName(newClassName); - // check this in advance although classNameChanged() below does. - classPool.checkNotFrozen(newClassName); - } - - super.replaceClassName(classnames); - ClassFile cf = getClassFile2(); - cf.renameClass(classnames); - eraseCache(); - - if (newClassName != null) - { - super.setName(newClassName); - classPool.classNameChanged(oldClassName, this); - } - } - - public void replaceClassName(String oldname, String newname) - throws RuntimeException - { - String thisname = getName(); - if (thisname.equals(oldname)) - setName(newname); - else - { - super.replaceClassName(oldname, newname); - getClassFile2().renameClass(oldname, newname); - eraseCache(); - } - } - - public boolean isInterface() - { - return Modifier.isInterface(getModifiers()); - } - - public int getModifiers() - { - int acc = getClassFile2().getAccessFlags(); - acc = AccessFlag.clear(acc, AccessFlag.SUPER); - return AccessFlag.toModifier(acc); - } - - public void setModifiers(int mod) - { - checkModify(); - int acc = AccessFlag.of(mod) | AccessFlag.SUPER; - getClassFile2().setAccessFlags(acc); - } - - public boolean subclassOf(CtClass superclass) - { - if (superclass == null) - return false; - - String superName = superclass.getName(); - CtClass curr = this; - try - { - while (curr != null) - { - if (curr.getName().equals(superName)) - return true; - - curr = curr.getSuperclass(); - } - } - catch (Exception ignored) - { - } - return false; - } - - public CtClass getSuperclass() throws NotFoundException - { - String supername = getClassFile2().getSuperclass(); - if (supername == null) - return null; - else - return classPool.get(supername); - } - - public void setSuperclass(CtClass clazz) throws CannotCompileException - { - checkModify(); - if (isInterface()) - addInterface(clazz); - else - getClassFile2().setSuperclass(clazz.getName()); - } - - public CtClass[] getInterfaces() throws NotFoundException - { - String[] ifs = getClassFile2().getInterfaces(); - int num = ifs.length; - CtClass[] ifc = new CtClass[num]; - for (int i = 0; i < num; ++i) - ifc[i] = classPool.get(ifs[i]); - - return ifc; - } - - public void setInterfaces(CtClass[] list) - { - checkModify(); - String[] ifs; - if (list == null) - ifs = new String[0]; - else - { - int num = list.length; - ifs = new String[num]; - for (int i = 0; i < num; ++i) - ifs[i] = list[i].getName(); - } - - getClassFile2().setInterfaces(ifs); - } - - public void addInterface(CtClass anInterface) - { - checkModify(); - if (anInterface != null) - getClassFile2().addInterface(anInterface.getName()); - } - - public CtClass getDeclaringClass() throws NotFoundException - { - ClassFile cf = getClassFile2(); - InnerClassesAttribute ica = (InnerClassesAttribute) cf.getAttribute(InnerClassesAttribute.tag); - if (ica == null) - return null; - - String name = getName(); - int n = ica.tableLength(); - for (int i = 0; i < n; ++i) - if (name.equals(ica.innerClass(i))) - return classPool.get(ica.outerClass(i)); - - return null; - } - - public CtClass makeNestedClass(String name, boolean isStatic) - { - if (!isStatic) - throw new RuntimeException("sorry, only nested static class is supported"); - - checkModify(); - CtClass c = classPool.makeNestedClass(getName() + "$" + name); - ClassFile cf = getClassFile2(); - InnerClassesAttribute ica = (InnerClassesAttribute) cf.getAttribute(InnerClassesAttribute.tag); - if (ica == null) - { - ica = new InnerClassesAttribute(cf.getConstPool()); - cf.addAttribute(ica); - } - - ica.append(c.getName(), this.getName(), name, AccessFlag.STATIC); - ClassFile cf2 = c.getClassFile2(); - cf2.addAttribute(ica.copy(cf2.getConstPool(), null)); - return c; - } - - public CtField[] getFields() - { - ArrayList alist = new ArrayList(); - getFields(alist, this); - return (CtField[]) alist.toArray(new CtField[alist.size()]); - } - - private static void getFields(ArrayList alist, CtClass cc) - { - int i, num; - if (cc == null) - return; - - try - { - getFields(alist, cc.getSuperclass()); - } - catch (NotFoundException e) - { - } - - try - { - CtClass[] ifs = cc.getInterfaces(); - num = ifs.length; - for (i = 0; i < num; ++i) - getFields(alist, ifs[i]); - } - catch (NotFoundException e) - { - } - - CtField cf = ((CtClassType) cc).getFieldsCache(); - while (cf != null) - { - if (Modifier.isPublic(cf.getModifiers())) - alist.add(cf); - - cf = cf.next; - } - } - - public CtField getField(String name) throws NotFoundException - { - CtField f = getField2(name); - if (f == null) - throw new NotFoundException("field: " + name + " in " + getName()); - else - return f; - } - - CtField getField2(String name) - { - CtField df = getDeclaredField2(name); - if (df != null) - return df; - - try - { - CtClass[] ifs = getInterfaces(); - int num = ifs.length; - for (int i = 0; i < num; ++i) - { - CtField f = ifs[i].getField2(name); - if (f != null) - return f; - } - - CtClass s = getSuperclass(); - if (s != null) - return s.getField2(name); - } - catch (NotFoundException e) - { - } - return null; - } - - public CtField[] getDeclaredFields() - { - CtField cf = getFieldsCache(); - int num = CtField.count(cf); - CtField[] cfs = new CtField[num]; - int i = 0; - while (cf != null) - { - cfs[i++] = cf; - cf = cf.next; - } - - return cfs; - } - - protected CtField getFieldsCache() - { - if (fieldsCache == null) - { - List list = getClassFile2().getFields(); - int n = list.size(); - for (int i = 0; i < n; ++i) - { - FieldInfo finfo = (FieldInfo) list.get(i); - fieldsCache = CtField.append(fieldsCache, - new CtField(finfo, this)); - } - } - - return fieldsCache; - } - - public CtField getDeclaredField(String name) throws NotFoundException - { - CtField f = getDeclaredField2(name); - if (f == null) - throw new NotFoundException("field: " + name + " in " + getName()); - else - return f; - } - - private CtField getDeclaredField2(String name) - { - CtField cf = getFieldsCache(); - while (cf != null) - { - if (cf.getName().equals(name)) - return cf; - - cf = cf.next; - } - - return null; - } - - public CtBehavior[] getDeclaredBehaviors() - { - CtConstructor cc = getConstructorsCache(); - CtMethod cm = getMethodsCache(); - int num = CtMethod.count(cm) + CtConstructor.count(cc); - CtBehavior[] cb = new CtBehavior[num]; - int i = 0; - while (cc != null) - { - cb[i++] = cc; - cc = cc.next; - } - - while (cm != null) - { - cb[i++] = cm; - cm = cm.next; - } - - return cb; - } - - public CtConstructor[] getConstructors() - { - CtConstructor[] cons = getDeclaredConstructors(); - if (cons.length == 0) - return cons; - - int n = 0; - int i = cons.length; - while (--i >= 0) - if (Modifier.isPublic(cons[i].getModifiers())) - ++n; - - CtConstructor[] result = new CtConstructor[n]; - n = 0; - i = cons.length; - while (--i >= 0) - { - CtConstructor c = cons[i]; - if (Modifier.isPublic(c.getModifiers())) - result[n++] = c; - } - - return result; - } - - public CtConstructor getConstructor(String desc) - throws NotFoundException - { - CtConstructor cc = getConstructorsCache(); - while (cc != null) - { - if (cc.getMethodInfo2().getDescriptor().equals(desc)) - return cc; - - cc = cc.next; - } - - return super.getConstructor(desc); - } - - public CtConstructor[] getDeclaredConstructors() - { - CtConstructor cc = getConstructorsCache(); - int num = CtConstructor.count(cc); - CtConstructor[] ccs = new CtConstructor[num]; - int i = 0; - while (cc != null) - { - ccs[i++] = cc; - cc = cc.next; - } - - return ccs; - } - - protected CtConstructor getConstructorsCache() - { - if (constructorsCache == null) - { - List list = getClassFile2().getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - if (minfo.isConstructor()) - constructorsCache - = CtConstructor.append(constructorsCache, - new CtConstructor(minfo, this)); - } - } - - return constructorsCache; - } - - public CtConstructor getClassInitializer() - { - if (classInitializerCache == null) - { - MethodInfo minfo = getClassFile2().getStaticInitializer(); - if (minfo != null) - classInitializerCache = new CtConstructor(minfo, this); - } - - return classInitializerCache; - } - - public CtMethod[] getMethods() - { - HashMap h = new HashMap(); - getMethods0(h, this); - return (CtMethod[]) h.values().toArray(new CtMethod[0]); - } - - private static void getMethods0(HashMap h, CtClass cc) - { - try - { - CtClass[] ifs = cc.getInterfaces(); - int size = ifs.length; - for (int i = 0; i < size; ++i) - getMethods0(h, ifs[i]); - } - catch (NotFoundException e) - { - } - - try - { - CtClass s = cc.getSuperclass(); - if (s != null) - getMethods0(h, s); - } - catch (NotFoundException e) - { - } - - if (cc instanceof CtClassType) - { - CtMethod cm = ((CtClassType) cc).getMethodsCache(); - while (cm != null) - { - if (Modifier.isPublic(cm.getModifiers())) - h.put(cm.getStringRep(), cm); + for (i = 0; i < num; ++i) + if (classPool.get(ifs[i]).subtypeOf(clazz)) + return true; + + return false; + } + + public void setName(String name) throws RuntimeException { + String oldname = getName(); + if (name.equals(oldname)) + return; + + // check this in advance although classNameChanged() below does. + classPool.checkNotFrozen(name); + ClassFile cf = getClassFile2(); + super.setName(name); + cf.setName(name); + eraseCache(); + classPool.classNameChanged(oldname, this); + } + + public void replaceClassName(ClassMap classnames) + throws RuntimeException + { + String oldClassName = getName(); + String newClassName + = (String)classnames.get(Descriptor.toJvmName(oldClassName)); + if (newClassName != null) { + newClassName = Descriptor.toJavaName(newClassName); + // check this in advance although classNameChanged() below does. + classPool.checkNotFrozen(newClassName); + } + + super.replaceClassName(classnames); + ClassFile cf = getClassFile2(); + cf.renameClass(classnames); + eraseCache(); + + if (newClassName != null) { + super.setName(newClassName); + classPool.classNameChanged(oldClassName, this); + } + } + + public void replaceClassName(String oldname, String newname) + throws RuntimeException + { + String thisname = getName(); + if (thisname.equals(oldname)) + setName(newname); + else { + super.replaceClassName(oldname, newname); + getClassFile2().renameClass(oldname, newname); + eraseCache(); + } + } + + public boolean isInterface() { + return Modifier.isInterface(getModifiers()); + } + + public int getModifiers() { + int acc = getClassFile2().getAccessFlags(); + acc = AccessFlag.clear(acc, AccessFlag.SUPER); + return AccessFlag.toModifier(acc); + } + + public void setModifiers(int mod) { + checkModify(); + int acc = AccessFlag.of(mod) | AccessFlag.SUPER; + getClassFile2().setAccessFlags(acc); + } + + public boolean subclassOf(CtClass superclass) { + if (superclass == null) + return false; + + String superName = superclass.getName(); + CtClass curr = this; + try { + while (curr != null) { + if (curr.getName().equals(superName)) + return true; + + curr = curr.getSuperclass(); + } + } + catch (Exception ignored) {} + return false; + } + + public CtClass getSuperclass() throws NotFoundException { + String supername = getClassFile2().getSuperclass(); + if (supername == null) + return null; + else + return classPool.get(supername); + } + + public void setSuperclass(CtClass clazz) throws CannotCompileException { + checkModify(); + if (isInterface()) + addInterface(clazz); + else + getClassFile2().setSuperclass(clazz.getName()); + } + + public CtClass[] getInterfaces() throws NotFoundException { + String[] ifs = getClassFile2().getInterfaces(); + int num = ifs.length; + CtClass[] ifc = new CtClass[num]; + for (int i = 0; i < num; ++i) + ifc[i] = classPool.get(ifs[i]); + + return ifc; + } + + public void setInterfaces(CtClass[] list) { + checkModify(); + String[] ifs; + if (list == null) + ifs = new String[0]; + else { + int num = list.length; + ifs = new String[num]; + for (int i = 0; i < num; ++i) + ifs[i] = list[i].getName(); + } + + getClassFile2().setInterfaces(ifs); + } + + public void addInterface(CtClass anInterface) { + checkModify(); + if (anInterface != null) + getClassFile2().addInterface(anInterface.getName()); + } + + public CtClass getDeclaringClass() throws NotFoundException { + ClassFile cf = getClassFile2(); + InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( + InnerClassesAttribute.tag); + if (ica == null) + return null; + + String name = getName(); + int n = ica.tableLength(); + for (int i = 0; i < n; ++i) + if (name.equals(ica.innerClass(i))) + return classPool.get(ica.outerClass(i)); + + return null; + } + + public CtClass makeNestedClass(String name, boolean isStatic) { + if (!isStatic) + throw new RuntimeException( + "sorry, only nested static class is supported"); + + checkModify(); + CtClass c = classPool.makeNestedClass(getName() + "$" + name); + ClassFile cf = getClassFile2(); + InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( + InnerClassesAttribute.tag); + if (ica == null) { + ica = new InnerClassesAttribute(cf.getConstPool()); + cf.addAttribute(ica); + } + + ica.append(c.getName(), this.getName(), name, AccessFlag.STATIC); + ClassFile cf2 = c.getClassFile2(); + cf2.addAttribute(ica.copy(cf2.getConstPool(), null)); + return c; + } + + public CtField[] getFields() { + ArrayList alist = new ArrayList(); + getFields(alist, this); + return (CtField[])alist.toArray(new CtField[alist.size()]); + } + + private static void getFields(ArrayList alist, CtClass cc) { + int i, num; + if (cc == null) + return; + + try { + getFields(alist, cc.getSuperclass()); + } + catch (NotFoundException e) {} + + try { + CtClass[] ifs = cc.getInterfaces(); + num = ifs.length; + for (i = 0; i < num; ++i) + getFields(alist, ifs[i]); + } + catch (NotFoundException e) {} + + CtField cf = ((CtClassType)cc).getFieldsCache(); + while (cf != null) { + if (Modifier.isPublic(cf.getModifiers())) + alist.add(cf); + + cf = cf.next; + } + } + + public CtField getField(String name) throws NotFoundException { + CtField f = getField2(name); + if (f == null) + throw new NotFoundException("field: " + name + " in " + getName()); + else + return f; + } + + CtField getField2(String name) { + CtField df = getDeclaredField2(name); + if (df != null) + return df; + + try { + CtClass[] ifs = getInterfaces(); + int num = ifs.length; + for (int i = 0; i < num; ++i) { + CtField f = ifs[i].getField2(name); + if (f != null) + return f; + } + CtClass s = getSuperclass(); + if (s != null) + return s.getField2(name); + } + catch (NotFoundException e) {} + return null; + } + + public CtField[] getDeclaredFields() { + CtField cf = getFieldsCache(); + int num = CtField.count(cf); + CtField[] cfs = new CtField[num]; + int i = 0; + while (cf != null) { + cfs[i++] = cf; + cf = cf.next; + } + + return cfs; + } + + protected CtField getFieldsCache() { + if (fieldsCache == null) { + List list = getClassFile2().getFields(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + fieldsCache = CtField.append(fieldsCache, + new CtField(finfo, this)); + } + } + + return fieldsCache; + } + + public CtField getDeclaredField(String name) throws NotFoundException { + CtField f = getDeclaredField2(name); + if (f == null) + throw new NotFoundException("field: " + name + " in " + getName()); + else + return f; + } + + private CtField getDeclaredField2(String name) { + CtField cf = getFieldsCache(); + while (cf != null) { + if (cf.getName().equals(name)) + return cf; + + cf = cf.next; + } + + return null; + } + + public CtBehavior[] getDeclaredBehaviors() { + CtConstructor cc = getConstructorsCache(); + CtMethod cm = getMethodsCache(); + int num = CtMethod.count(cm) + CtConstructor.count(cc); + CtBehavior[] cb = new CtBehavior[num]; + int i = 0; + while (cc != null) { + cb[i++] = cc; + cc = cc.next; + } + + while (cm != null) { + cb[i++] = cm; cm = cm.next; - } - } - } - - public CtMethod getMethod(String name, String desc) - throws NotFoundException - { - CtMethod m = getMethod0(this, name, desc); - if (m != null) - return m; - else - throw new NotFoundException(name + "(..) is not found in " - + getName()); - } - - private static CtMethod getMethod0(CtClass cc, - String name, String desc) - { - if (cc instanceof CtClassType) - { - CtMethod cm = ((CtClassType) cc).getMethodsCache(); - while (cm != null) - { - if (cm.getName().equals(name) + } + + return cb; + } + + public CtConstructor[] getConstructors() { + CtConstructor[] cons = getDeclaredConstructors(); + if (cons.length == 0) + return cons; + + int n = 0; + int i = cons.length; + while (--i >= 0) + if (Modifier.isPublic(cons[i].getModifiers())) + ++n; + + CtConstructor[] result = new CtConstructor[n]; + n = 0; + i = cons.length; + while (--i >= 0) { + CtConstructor c = cons[i]; + if (Modifier.isPublic(c.getModifiers())) + result[n++] = c; + } + + return result; + } + + public CtConstructor getConstructor(String desc) + throws NotFoundException + { + CtConstructor cc = getConstructorsCache(); + while (cc != null) { + if (cc.getMethodInfo2().getDescriptor().equals(desc)) + return cc; + + cc = cc.next; + } + + return super.getConstructor(desc); + } + + public CtConstructor[] getDeclaredConstructors() { + CtConstructor cc = getConstructorsCache(); + int num = CtConstructor.count(cc); + CtConstructor[] ccs = new CtConstructor[num]; + int i = 0; + while (cc != null) { + ccs[i++] = cc; + cc = cc.next; + } + + return ccs; + } + + protected CtConstructor getConstructorsCache() { + if (constructorsCache == null) { + List list = getClassFile2().getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.isConstructor()) + constructorsCache + = CtConstructor.append(constructorsCache, + new CtConstructor(minfo, this)); + } + } + + return constructorsCache; + } + + public CtConstructor getClassInitializer() { + if (classInitializerCache == null) { + MethodInfo minfo = getClassFile2().getStaticInitializer(); + if (minfo != null) + classInitializerCache = new CtConstructor(minfo, this); + } + + return classInitializerCache; + } + + public CtMethod[] getMethods() { + HashMap h = new HashMap(); + getMethods0(h, this); + return (CtMethod[])h.values().toArray(new CtMethod[0]); + } + + private static void getMethods0(HashMap h, CtClass cc) { + try { + CtClass[] ifs = cc.getInterfaces(); + int size = ifs.length; + for (int i = 0; i < size; ++i) + getMethods0(h, ifs[i]); + } + catch (NotFoundException e) {} + + try { + CtClass s = cc.getSuperclass(); + if (s != null) + getMethods0(h, s); + } + catch (NotFoundException e) {} + + if (cc instanceof CtClassType) { + CtMethod cm = ((CtClassType)cc).getMethodsCache(); + while (cm != null) { + if (Modifier.isPublic(cm.getModifiers())) + h.put(cm.getStringRep(), cm); + + cm = cm.next; + } + } + } + + public CtMethod getMethod(String name, String desc) + throws NotFoundException + { + CtMethod m = getMethod0(this, name, desc); + if (m != null) + return m; + else + throw new NotFoundException(name + "(..) is not found in " + + getName()); + } + + private static CtMethod getMethod0(CtClass cc, + String name, String desc) { + if (cc instanceof CtClassType) { + CtMethod cm = ((CtClassType)cc).getMethodsCache(); + while (cm != null) { + if (cm.getName().equals(name) && cm.getMethodInfo2().getDescriptor().equals(desc)) - return cm; + return cm; + cm = cm.next; + } + } + + try { + CtClass s = cc.getSuperclass(); + if (s != null) { + CtMethod m = getMethod0(s, name, desc); + if (m != null) + return m; + } + } + catch (NotFoundException e) {} + + try { + CtClass[] ifs = cc.getInterfaces(); + int size = ifs.length; + for (int i = 0; i < size; ++i) { + CtMethod m = getMethod0(ifs[i], name, desc); + if (m != null) + return m; + } + } + catch (NotFoundException e) {} + return null; + } + + public CtMethod[] getDeclaredMethods() { + CtMethod cm = getMethodsCache(); + int num = CtMethod.count(cm); + CtMethod[] cms = new CtMethod[num]; + int i = 0; + while (cm != null) { + cms[i++] = cm; cm = cm.next; - } - } - - try - { - CtClass s = cc.getSuperclass(); - if (s != null) - { - CtMethod m = getMethod0(s, name, desc); - if (m != null) - return m; - } - } - catch (NotFoundException e) - { - } - - try - { - CtClass[] ifs = cc.getInterfaces(); - int size = ifs.length; - for (int i = 0; i < size; ++i) - { - CtMethod m = getMethod0(ifs[i], name, desc); - if (m != null) - return m; - } - } - catch (NotFoundException e) - { - } - return null; - } - - public CtMethod[] getDeclaredMethods() - { - CtMethod cm = getMethodsCache(); - int num = CtMethod.count(cm); - CtMethod[] cms = new CtMethod[num]; - int i = 0; - while (cm != null) - { - cms[i++] = cm; - cm = cm.next; - } - - return cms; - } - - public CtMethod getDeclaredMethod(String name) throws NotFoundException - { - CtMethod m = getMethodsCache(); - while (m != null) - { - if (m.getName().equals(name)) - return m; - - m = m.next; - } - - throw new NotFoundException(name + "(..) is not found in " - + getName()); - } - - public CtMethod getDeclaredMethod(String name, CtClass[] params) - throws NotFoundException - { - String desc = Descriptor.ofParameters(params); - CtMethod m = getMethodsCache(); - while (m != null) - { - if (m.getName().equals(name) - && m.getMethodInfo2().getDescriptor().startsWith(desc)) - return m; + } + + return cms; + } + + public CtMethod getDeclaredMethod(String name) throws NotFoundException { + CtMethod m = getMethodsCache(); + while (m != null) { + if (m.getName().equals(name)) + return m; + + m = m.next; + } + + throw new NotFoundException(name + "(..) is not found in " + + getName()); + } + + public CtMethod getDeclaredMethod(String name, CtClass[] params) + throws NotFoundException + { + String desc = Descriptor.ofParameters(params); + CtMethod m = getMethodsCache(); + while (m != null) { + if (m.getName().equals(name) + && m.getMethodInfo2().getDescriptor().startsWith(desc)) + return m; + + m = m.next; + } + + throw new NotFoundException(name + "(..) is not found in " + + getName()); + } + + protected CtMethod getMethodsCache() { + if (methodsCache == null) { + List list = getClassFile2().getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.isMethod()) + methodsCache = CtMethod.append(methodsCache, + new CtMethod(minfo, this)); + } + } + + return methodsCache; + } + + public void addField(CtField f, String init) + throws CannotCompileException + { + addField(f, CtField.Initializer.byExpr(init)); + } + + public void addField(CtField f, CtField.Initializer init) + throws CannotCompileException + { + checkModify(); + if (f.getDeclaringClass() != this) + throw new CannotCompileException("cannot add"); + + if (init == null) + init = f.getInit(); + + getFieldsCache(); + fieldsCache = CtField.append(fieldsCache, f); + getClassFile2().addField(f.getFieldInfo2()); + + if (init != null) { + FieldInitLink fil = new FieldInitLink(f, init); + FieldInitLink link = fieldInitializers; + if (link == null) + fieldInitializers = fil; + else { + while (link.next != null) + link = link.next; + + link.next = fil; + } + } + } + + public CtConstructor makeClassInitializer() + throws CannotCompileException + { + CtConstructor clinit = getClassInitializer(); + if (clinit != null) + return clinit; + + checkModify(); + ClassFile cf = getClassFile2(); + Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); + modifyClassConstructor(cf, code, 0, 0); + return getClassInitializer(); + } + + public void addConstructor(CtConstructor c) + throws CannotCompileException + { + checkModify(); + if (c.getDeclaringClass() != this) + throw new CannotCompileException("cannot add"); + + getConstructorsCache(); + constructorsCache = CtConstructor.append(constructorsCache, c); + getClassFile2().addMethod(c.getMethodInfo2()); + } + + public void addMethod(CtMethod m) throws CannotCompileException { + checkModify(); + if (m.getDeclaringClass() != this) + throw new CannotCompileException("cannot add"); + + getMethodsCache(); + methodsCache = CtMethod.append(methodsCache, m); + getClassFile2().addMethod(m.getMethodInfo2()); + if ((m.getModifiers() & Modifier.ABSTRACT) != 0) + setModifiers(getModifiers() | Modifier.ABSTRACT); + } + + public byte[] getAttribute(String name) { + AttributeInfo ai = getClassFile2().getAttribute(name); + if (ai == null) + return null; + else + return ai.get(); + } + + public void setAttribute(String name, byte[] data) { + checkModify(); + ClassFile cf = getClassFile2(); + cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data)); + } + + public void instrument(CodeConverter converter) + throws CannotCompileException + { + checkModify(); + ClassFile cf = getClassFile2(); + ConstPool cp = cf.getConstPool(); + List list = cf.getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + converter.doit(this, minfo, cp); + } + } + + public void instrument(ExprEditor editor) + throws CannotCompileException + { + checkModify(); + ClassFile cf = getClassFile2(); + List list = cf.getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + editor.doit(this, minfo); + } + } + + public void toBytecode(DataOutputStream out) + throws CannotCompileException, IOException + { + 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 { + classPool.writeClassfile(getName(), out); + // to save memory + eraseCache(); + classfile = null; + } - m = m.next; - } - - throw new NotFoundException(name + "(..) is not found in " - + getName()); - } - - protected CtMethod getMethodsCache() - { - if (methodsCache == null) - { - List list = getClassFile2().getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - if (minfo.isMethod()) - methodsCache = CtMethod.append(methodsCache, - new CtMethod(minfo, this)); - } - } - - return methodsCache; - } - - public void addField(CtField f, String init) - throws CannotCompileException - { - addField(f, CtField.Initializer.byExpr(init)); - } - - public void addField(CtField f, CtField.Initializer init) - throws CannotCompileException - { - checkModify(); - if (f.getDeclaringClass() != this) - throw new CannotCompileException("cannot add"); - - if (init == null) - init = f.getInit(); - - getFieldsCache(); - fieldsCache = CtField.append(fieldsCache, f); - getClassFile2().addField(f.getFieldInfo2()); - - if (init != null) - { - FieldInitLink fil = new FieldInitLink(f, init); - FieldInitLink link = fieldInitializers; - if (link == null) - fieldInitializers = fil; - else - { - while (link.next != null) - link = link.next; - - link.next = fil; - } - } - } - - public CtConstructor makeClassInitializer() - throws CannotCompileException - { - CtConstructor clinit = getClassInitializer(); - if (clinit != null) - return clinit; - - checkModify(); - ClassFile cf = getClassFile2(); - Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); - modifyClassConstructor(cf, code, 0, 0); - return getClassInitializer(); - } - - public void addConstructor(CtConstructor c) - throws CannotCompileException - { - checkModify(); - if (c.getDeclaringClass() != this) - throw new CannotCompileException("cannot add"); - - getConstructorsCache(); - constructorsCache = CtConstructor.append(constructorsCache, c); - getClassFile2().addMethod(c.getMethodInfo2()); - } - - public void addMethod(CtMethod m) throws CannotCompileException - { - checkModify(); - if (m.getDeclaringClass() != this) - throw new CannotCompileException("cannot add"); - - getMethodsCache(); - methodsCache = CtMethod.append(methodsCache, m); - getClassFile2().addMethod(m.getMethodInfo2()); - if ((m.getModifiers() & Modifier.ABSTRACT) != 0) - setModifiers(getModifiers() | Modifier.ABSTRACT); - } - - public byte[] getAttribute(String name) - { - AttributeInfo ai = getClassFile2().getAttribute(name); - if (ai == null) - return null; - else - return ai.get(); - } - - public void setAttribute(String name, byte[] data) - { - checkModify(); - ClassFile cf = getClassFile2(); - cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data)); - } - - public void instrument(CodeConverter converter) - throws CannotCompileException - { - checkModify(); - ClassFile cf = getClassFile2(); - ConstPool cp = cf.getConstPool(); - List list = cf.getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - converter.doit(this, minfo, cp); - } - } - - public void instrument(ExprEditor editor) - throws CannotCompileException - { - checkModify(); - ClassFile cf = getClassFile2(); - List list = cf.getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - editor.doit(this, minfo); - } - } - - public void toBytecode(DataOutputStream out) - throws CannotCompileException, IOException - { - 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; + wasFrozen = true; + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (IOException e) { + throw new CannotCompileException(e); + } + } + + 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 (fieldInitializers == null) + return; + + Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); + Javac jv = new Javac(code, this); + int stacksize = 0; + boolean doInit = false; + for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { + CtField f = fi.field; + if (Modifier.isStatic(f.getModifiers())) { + doInit = true; + int s = fi.init.compileIfStatic(f.getType(), f.getName(), + code, jv); + if (stacksize < s) + stacksize = s; } - } - else - { - classPool.writeClassfile(getName(), out); - // to save memory - eraseCache(); - classfile = null; - } - - wasFrozen = true; - } - catch (NotFoundException e) - { - throw new CannotCompileException(e); - } - catch (IOException e) - { - throw new CannotCompileException(e); - } - } - - 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 (fieldInitializers == null) - return; - - Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); - Javac jv = new Javac(code, this); - int stacksize = 0; - boolean doInit = false; - for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) - { - CtField f = fi.field; - if (Modifier.isStatic(f.getModifiers())) - { - doInit = true; - int s = fi.init.compileIfStatic(f.getType(), f.getName(), - code, jv); - if (stacksize < s) - stacksize = s; - } - } - - if (doInit) // need an initializer for static fileds. - modifyClassConstructor(cf, code, stacksize, 0); - } - - private void modifyClassConstructor(ClassFile cf, Bytecode code, - int stacksize, int localsize) - throws CannotCompileException - { - MethodInfo m = cf.getStaticInitializer(); - if (m == null) - { - code.add(Bytecode.RETURN); - code.setMaxStack(stacksize); - code.setMaxLocals(localsize); - m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V"); - m.setAccessFlags(AccessFlag.STATIC); - m.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(m); - } - else - { - CodeAttribute codeAttr = m.getCodeAttribute(); - if (codeAttr == null) - throw new CannotCompileException("empty <clinit>"); - - try - { - CodeIterator it = codeAttr.iterator(); - int pos = it.insertEx(code.get()); - it.insert(code.getExceptionTable(), pos); - int maxstack = codeAttr.getMaxStack(); - if (maxstack < stacksize) - codeAttr.setMaxStack(stacksize); - - int maxlocals = codeAttr.getMaxLocals(); - if (maxlocals < localsize) - codeAttr.setMaxLocals(localsize); - } - catch (BadBytecode e) - { + } + + if (doInit) // need an initializer for static fileds. + modifyClassConstructor(cf, code, stacksize, 0); + } + + private void modifyClassConstructor(ClassFile cf, Bytecode code, + int stacksize, int localsize) + throws CannotCompileException + { + MethodInfo m = cf.getStaticInitializer(); + if (m == null) { + code.add(Bytecode.RETURN); + code.setMaxStack(stacksize); + code.setMaxLocals(localsize); + m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V"); + m.setAccessFlags(AccessFlag.STATIC); + m.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(m); + } + else { + CodeAttribute codeAttr = m.getCodeAttribute(); + if (codeAttr == null) + throw new CannotCompileException("empty <clinit>"); + + try { + CodeIterator it = codeAttr.iterator(); + int pos = it.insertEx(code.get()); + it.insert(code.getExceptionTable(), pos); + int maxstack = codeAttr.getMaxStack(); + if (maxstack < stacksize) + codeAttr.setMaxStack(stacksize); + + int maxlocals = codeAttr.getMaxLocals(); + if (maxlocals < localsize) + codeAttr.setMaxLocals(localsize); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + } + + private void modifyConstructors(ClassFile cf) + throws CannotCompileException, NotFoundException + { + if (fieldInitializers == null) + return; + + ConstPool cp = cf.getConstPool(); + List list = cf.getMethods(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.isConstructor()) { + CodeAttribute codeAttr = minfo.getCodeAttribute(); + if (codeAttr != null) + try { + Bytecode init = new Bytecode(cp, 0, + codeAttr.getMaxLocals()); + CtClass[] params + = Descriptor.getParameterTypes( + minfo.getDescriptor(), + classPool); + int stacksize = makeFieldInitializer(init, params); + insertAuxInitializer(codeAttr, init, stacksize); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + } + } + + private static void insertAuxInitializer(CodeAttribute codeAttr, + Bytecode initializer, + int stacksize) + throws BadBytecode + { + CodeIterator it = codeAttr.iterator(); + int index = it.skipSuperConstructor(); + if (index < 0) { + index = it.skipThisConstructor(); + if (index >= 0) + return; // this() is called. + + // Neither this() or super() is called. + } + + int pos = it.insertEx(initializer.get()); + it.insert(initializer.getExceptionTable(), pos); + int maxstack = codeAttr.getMaxStack(); + if (maxstack < stacksize) + codeAttr.setMaxStack(stacksize); + } + + private int makeFieldInitializer(Bytecode code, CtClass[] parameters) + throws CannotCompileException, NotFoundException + { + int stacksize = 0; + Javac jv = new Javac(code, this); + try { + jv.recordParams(parameters, false); + } + catch (CompileError e) { throw new CannotCompileException(e); - } - } - } - - private void modifyConstructors(ClassFile cf) - throws CannotCompileException, NotFoundException - { - if (fieldInitializers == null) - return; - - ConstPool cp = cf.getConstPool(); - List list = cf.getMethods(); - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - if (minfo.isConstructor()) - { - CodeAttribute codeAttr = minfo.getCodeAttribute(); - if (codeAttr != null) - try - { - Bytecode init = new Bytecode(cp, 0, - codeAttr.getMaxLocals()); - CtClass[] params - = Descriptor.getParameterTypes(minfo.getDescriptor(), - classPool); - int stacksize = makeFieldInitializer(init, params); - insertAuxInitializer(codeAttr, init, stacksize); - } - catch (BadBytecode e) - { - throw new CannotCompileException(e); - } - } - } - } - - private static void insertAuxInitializer(CodeAttribute codeAttr, - Bytecode initializer, - int stacksize) - throws BadBytecode - { - CodeIterator it = codeAttr.iterator(); - int index = it.skipSuperConstructor(); - if (index < 0) - { - index = it.skipThisConstructor(); - if (index >= 0) - return; // this() is called. - - // Neither this() or super() is called. - } - - int pos = it.insertEx(initializer.get()); - it.insert(initializer.getExceptionTable(), pos); - int maxstack = codeAttr.getMaxStack(); - if (maxstack < stacksize) - codeAttr.setMaxStack(stacksize); - } - - private int makeFieldInitializer(Bytecode code, CtClass[] parameters) - throws CannotCompileException, NotFoundException - { - int stacksize = 0; - Javac jv = new Javac(code, this); - try - { - jv.recordParams(parameters, false); - } - catch (CompileError e) - { - throw new CannotCompileException(e); - } - - for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) - { - CtField f = fi.field; - if (!Modifier.isStatic(f.getModifiers())) - { - int s = fi.init.compile(f.getType(), f.getName(), code, - parameters, jv); - if (stacksize < s) - stacksize = s; - } - } - - return stacksize; - } - - // Methods used by CtNewWrappedMethod - - Hashtable getHiddenMethods() - { - if (hiddenMethods == null) - hiddenMethods = new Hashtable(); - - return hiddenMethods; - } - - int getUniqueNumber() - { - return uniqueNumberSeed++; - } + } + + for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { + CtField f = fi.field; + if (!Modifier.isStatic(f.getModifiers())) { + int s = fi.init.compile(f.getType(), f.getName(), code, + parameters, jv); + if (stacksize < s) + stacksize = s; + } + } + + return stacksize; + } + + // Methods used by CtNewWrappedMethod + + Hashtable getHiddenMethods() { + if (hiddenMethods == null) + hiddenMethods = new Hashtable(); + + return hiddenMethods; + } + + int getUniqueNumber() { return uniqueNumberSeed++; } } -class FieldInitLink -{ - FieldInitLink next; - CtField field; - CtField.Initializer init; - - FieldInitLink(CtField f, CtField.Initializer i) - { - next = null; - field = f; - init = i; - } +class FieldInitLink { + FieldInitLink next; + CtField field; + CtField.Initializer init; + + FieldInitLink(CtField f, CtField.Initializer i) { + next = null; + field = f; + init = i; + } } diff --git a/src/main/javassist/bytecode/AnnotationsAttribute.java b/src/main/javassist/bytecode/AnnotationsAttribute.java index 2483c01b..302004a6 100644 --- a/src/main/javassist/bytecode/AnnotationsAttribute.java +++ b/src/main/javassist/bytecode/AnnotationsAttribute.java @@ -15,34 +15,17 @@ package javassist.bytecode; -import javassist.bytecode.annotation.Annotation; -import javassist.bytecode.annotation.AnnotationMemberValue; -import javassist.bytecode.annotation.AnnotationsWriter; -import javassist.bytecode.annotation.ArrayMemberValue; -import javassist.bytecode.annotation.BooleanMemberValue; -import javassist.bytecode.annotation.ByteMemberValue; -import javassist.bytecode.annotation.CharMemberValue; -import javassist.bytecode.annotation.ClassMemberValue; -import javassist.bytecode.annotation.DoubleMemberValue; -import javassist.bytecode.annotation.EnumMemberValue; -import javassist.bytecode.annotation.FloatMemberValue; -import javassist.bytecode.annotation.IntegerMemberValue; -import javassist.bytecode.annotation.LongMemberValue; -import javassist.bytecode.annotation.MemberValue; -import javassist.bytecode.annotation.ShortMemberValue; -import javassist.bytecode.annotation.StringMemberValue; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.HashMap; import java.util.Map; +import java.io.IOException; +import java.io.DataInputStream; +import java.io.ByteArrayOutputStream; +import javassist.bytecode.annotation.*; /** * A class representing * <code>RuntimeVisibleAnnotations_attribute</code> and * <code>RuntimeInvisibleAnnotations_attribute</code>. - * <p/> + * * <p>To obtain an AnnotationAttribute object, invoke * <code>getAttribute(AnnotationsAttribute.invisibleTag)</code> * in <code>ClassFile</code>, <code>MethodInfo</code>, @@ -51,10 +34,10 @@ import java.util.Map; * If the parameter is * <code>AnnotationAttribute.visibleTag</code>, then the obtained * attribute is a runtime visible one. - * <p/> + * * <p>If you want to record a new AnnotationAttribute object, execute the * following snippet: - * <p/> + * * <ul><pre> * ClassFile cf = ... ; * ConstPool cp = cf.getConstPool(); @@ -66,571 +49,513 @@ import java.util.Map; * cf.addAttribute(attr); * </pre></ul> */ -public class AnnotationsAttribute extends AttributeInfo -{ - /** - * The name of the <code>RuntimeVisibleAnnotations</code> attribute. - */ - public static final String visibleTag = "RuntimeVisibleAnnotations"; - - /** - * The name of the <code>RuntimeInvisibleAnnotations</code> attribute. - */ - public static final String invisibleTag = "RuntimeInvisibleAnnotations"; - - private HashMap annotationMap; - - /** - * Constructs a <code>Runtime(In)VisisbleAnnotations_attribute</code>. - * - * @param cp constant pool - * @param attrname attribute name (<code>visibleTag</code> or - * <code>invisibleTag</code>). - * @param info the contents of this attribute. It does not - * include <code>attribute_name_index</code> or - * <code>attribute_length</code>. - */ - public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) - { - super(cp, attrname, info); - } - - /** - * Constructs an empty - * <code>Runtime(In)VisisbleAnnotations_attribute</code>. - * A new annotation can be later added to the created attribute - * by <code>setAnnotations()</code>. - * - * @param cp constant pool - * @param attrname attribute name (<code>visibleTag</code> or - * <code>invisibleTag</code>). - * @see #setAnnotations(Annotation[]) - */ - public AnnotationsAttribute(ConstPool cp, String attrname) - { - this(cp, attrname, new byte[]{0, 0}); - } - - /** - * @param n the attribute name. - */ - AnnotationsAttribute(ConstPool cp, int n, DataInputStream in) - throws IOException - { - super(cp, n, in); - } - - /** - * Returns <code>num_annotations</code>. - */ - public int numAnnotations() - { - return ByteArray.readU16bit(info, 0); - } - - /** - * Copies this attribute and returns a new copy. - */ - public AttributeInfo copy(ConstPool newCp, Map classnames) - { - Copier copier = new Copier(info, constPool, newCp, classnames); - try - { - copier.annotationArray(); - return new AnnotationsAttribute(newCp, getName(), copier.close()); - } - catch (Exception e) - { - throw new RuntimeException(e.toString()); - } - } - - public Annotation getAnnotation(String type) - { - Annotation[] annotations = getAnnotations(); - for (int i = 0; i < annotations.length; i++) - { - if (annotations[i].getTypeName().equals(type)) return annotations[i]; - } - return null; - } - - public void addAnnotation(Annotation annotation) - { - String type = annotation.getTypeName(); - Annotation[] annotations = getAnnotations(); - for (int i = 0; i < annotations.length; i++) - { - if (annotations[i].getTypeName().equals(type)) - { - annotations[i] = annotation; - setAnnotations(annotations); - return; - } - } - Annotation[] newlist = new Annotation[annotations.length + 1]; - System.arraycopy(annotations, 0, newlist, 0, annotations.length); - newlist[annotations.length] = annotation; - setAnnotations(newlist); - } - - /** - * Parses the annotations and returns a data structure representing - * that parsed annotations. Note that changes of the node values of the - * returned tree are not reflected on the annotations represented by - * this object unless the tree is copied back to this object by - * <code>setAnnotations()</code>. - * - * @see #setAnnotations(Annotation[]) - */ - public Annotation[] getAnnotations() - { - try - { - return new Parser(info, constPool).parseAnnotations(); - } - catch (Exception e) - { - throw new RuntimeException(e.toString()); - } - } - - /** - * Changes the annotations represented by this object according to - * the given array of <code>Annotation</code> objects. - * - * @param annotations the data structure representing the - * new annotations. - */ - public void setAnnotations(Annotation[] annotations) - { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - AnnotationsWriter writer = new AnnotationsWriter(output, constPool); - try - { - int n = annotations.length; - writer.numAnnotations(n); - for (int i = 0; i < n; ++i) - annotations[i].write(writer); - - writer.close(); - } - catch (IOException e) - { - throw new RuntimeException(e); // should never reach here. - } - - set(output.toByteArray()); - } - - /** - * Changes the annotations. A call to this method is equivalent to: - * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul> - * - * @param annotation the data structure representing - * the new annotation. - */ - public void setAnnotation(Annotation annotation) - { - setAnnotations(new Annotation[]{annotation}); - } - - /** - * Returns a string representation of this object. - */ - public String toString() - { - Annotation[] a = getAnnotations(); - StringBuffer sbuf = new StringBuffer(); - int i = 0; - while (i < a.length) - { - sbuf.append(a[i++].toString()); - if (i != a.length) - sbuf.append(", "); - } - - return sbuf.toString(); - } - - static class Walker - { - byte[] info; - - Walker(byte[] attrInfo) - { - info = attrInfo; - } - - final void parameters() throws Exception - { - int numParam = info[0] & 0xff; - parameters(numParam, 1); - } - - void parameters(int numParam, int pos) throws Exception - { - for (int i = 0; i < numParam; ++i) - pos = annotationArray(pos); - } - - final void annotationArray() throws Exception - { - annotationArray(0); - } - - final int annotationArray(int pos) throws Exception - { - int num = ByteArray.readU16bit(info, pos); - return annotationArray(pos + 2, num); - } - - int annotationArray(int pos, int num) throws Exception - { - for (int i = 0; i < num; ++i) - pos = annotation(pos); - - return pos; - } - - final int annotation(int pos) throws Exception - { - int type = ByteArray.readU16bit(info, pos); - int numPairs = ByteArray.readU16bit(info, pos + 2); - return annotation(pos + 4, type, numPairs); - } - - int annotation(int pos, int type, int numPairs) throws Exception - { - for (int j = 0; j < numPairs; ++j) - pos = memberValuePair(pos); - - return pos; - } - - final int memberValuePair(int pos) throws Exception - { - int nameIndex = ByteArray.readU16bit(info, pos); - return memberValuePair(pos + 2, nameIndex); - } - - int memberValuePair(int pos, int nameIndex) throws Exception - { - return memberValue(pos); - } - - final int memberValue(int pos) throws Exception - { - int tag = info[pos] & 0xff; - if (tag == 'e') - { - int typeNameIndex = ByteArray.readU16bit(info, pos + 1); - int constNameIndex = ByteArray.readU16bit(info, pos + 3); - enumMemberValue(typeNameIndex, constNameIndex); - return pos + 5; - } - else if (tag == 'c') - { - int index = ByteArray.readU16bit(info, pos + 1); - classMemberValue(index); - return pos + 3; - } - else if (tag == '@') - return annotationMemberValue(pos + 1); - else if (tag == '[') - { - int num = ByteArray.readU16bit(info, pos + 1); - return arrayMemberValue(pos + 3, num); - } - else - { // primitive types or String. - int index = ByteArray.readU16bit(info, pos + 1); - constValueMember(tag, index); - return pos + 3; - } - } - - void constValueMember(int tag, int index) throws Exception - { - } - - void enumMemberValue(int typeNameIndex, int constNameIndex) - throws Exception - { - } - - void classMemberValue(int index) throws Exception - { - } - - int annotationMemberValue(int pos) throws Exception - { - return annotation(pos); - } - - int arrayMemberValue(int pos, int num) throws Exception - { - for (int i = 0; i < num; ++i) - { - pos = memberValue(pos); - } - - return pos; - } - } - - static class Copier extends Walker - { - ByteArrayOutputStream output; - AnnotationsWriter writer; - ConstPool srcPool, destPool; - Map classnames; - - /** - * Constructs a copier. This copier renames some class names - * into the new names specified by <code>map</code> when it copies - * an annotation attribute. - * - * @param info the source attribute. - * @param src the constant pool of the source class. - * @param dest the constant pool of the destination class. - * @param map pairs of replaced and substituted class names. - * It can be null. - */ - Copier(byte[] info, ConstPool src, ConstPool dest, Map map) - { - super(info); - output = new ByteArrayOutputStream(); - writer = new AnnotationsWriter(output, dest); - srcPool = src; - destPool = dest; - classnames = map; - } - - byte[] close() throws IOException - { - writer.close(); - return output.toByteArray(); - } - - void parameters(int numParam, int pos) throws Exception - { - writer.numParameters(numParam); - super.parameters(numParam, pos); - } - - int annotationArray(int pos, int num) throws Exception - { - writer.numAnnotations(num); - return super.annotationArray(pos, num); - } - - int annotation(int pos, int type, int numPairs) throws Exception - { - writer.annotation(copy(type), numPairs); - return super.annotation(pos, type, numPairs); - } - - int memberValuePair(int pos, int nameIndex) throws Exception - { - writer.memberValuePair(copy(nameIndex)); - return super.memberValuePair(pos, nameIndex); - } - - void constValueMember(int tag, int index) throws Exception - { - writer.constValueIndex(tag, copy(index)); - super.constValueMember(tag, index); - } - - void enumMemberValue(int typeNameIndex, int constNameIndex) - throws Exception - { - writer.enumConstValue(copy(typeNameIndex), copy(constNameIndex)); - super.enumMemberValue(typeNameIndex, constNameIndex); - } - - void classMemberValue(int index) throws Exception - { - writer.classInfoIndex(copy(index)); - super.classMemberValue(index); - } - - int annotationMemberValue(int pos) throws Exception - { - writer.annotationValue(); - return super.annotationMemberValue(pos); - } - - int arrayMemberValue(int pos, int num) throws Exception - { - writer.arrayValue(num); - return super.arrayMemberValue(pos, num); - } - - /** - * Copies a constant pool entry into the destination constant pool - * and returns the index of the copied entry. - * - * @param srcIndex the index of the copied entry into the source - * constant pool. - * @return the index of the copied item into the destination - * constant pool. - */ - int copy(int srcIndex) - { - return srcPool.copy(srcIndex, destPool, classnames); - } - } - - static class Parser extends Walker - { - ConstPool pool; - Annotation[][] allParams; // all parameters - Annotation[] allAnno; // all annotations - Annotation currentAnno; // current annotation - MemberValue memberValue; - - /** - * Constructs a parser. This parser constructs a parse tree of - * the annotations. - * - * @param info the attribute. - * @param src the constant pool. - */ - Parser(byte[] info, ConstPool cp) - { - super(info); - pool = cp; - } - - Annotation[][] parseParameters() throws Exception - { - parameters(); - return allParams; - } - - Annotation[] parseAnnotations() throws Exception - { - annotationArray(); - return allAnno; - } - - void parameters(int numParam, int pos) throws Exception - { - Annotation[][] params = new Annotation[numParam][]; - for (int i = 0; i < numParam; ++i) - { - pos = annotationArray(pos); - params[i] = allAnno; - } - - allParams = params; - } - - int annotationArray(int pos, int num) throws Exception - { - Annotation[] array = new Annotation[num]; - for (int i = 0; i < num; ++i) - { - pos = annotation(pos); - array[i] = currentAnno; - } - - allAnno = array; - return pos; - } - - int annotation(int pos, int type, int numPairs) throws Exception - { - currentAnno = new Annotation(type, pool); - return super.annotation(pos, type, numPairs); - } - - int memberValuePair(int pos, int nameIndex) throws Exception - { - pos = super.memberValuePair(pos, nameIndex); - currentAnno.addMemberValue(nameIndex, memberValue); - return pos; - } - - void constValueMember(int tag, int index) throws Exception - { - MemberValue m; - ConstPool cp = pool; - switch (tag) - { - case 'B': - m = new ByteMemberValue(index, cp); - break; - case 'C': - m = new CharMemberValue(index, cp); - break; - case 'D': - m = new DoubleMemberValue(index, cp); - break; - case 'F': - m = new FloatMemberValue(index, cp); - break; - case 'I': - m = new IntegerMemberValue(index, cp); - break; - case 'J': - m = new LongMemberValue(index, cp); - break; - case 'S': - m = new ShortMemberValue(index, cp); - break; - case 'Z': - m = new BooleanMemberValue(index, cp); - break; - case 's': - m = new StringMemberValue(index, cp); - break; - default : - throw new RuntimeException("unknown tag:" + tag); - } - - memberValue = m; - super.constValueMember(tag, index); - } - - void enumMemberValue(int typeNameIndex, int constNameIndex) - throws Exception - { - memberValue = new EnumMemberValue(typeNameIndex, - constNameIndex, pool); - super.enumMemberValue(typeNameIndex, constNameIndex); - } - - void classMemberValue(int index) throws Exception - { - memberValue = new ClassMemberValue(index, pool); - super.classMemberValue(index); - } - - int annotationMemberValue(int pos) throws Exception - { - Annotation anno = currentAnno; - pos = super.annotationMemberValue(pos); - memberValue = new AnnotationMemberValue(currentAnno, pool); - currentAnno = anno; - return pos; - } - - int arrayMemberValue(int pos, int num) throws Exception - { - ArrayMemberValue amv = new ArrayMemberValue(pool); - MemberValue[] elements = new MemberValue[num]; - for (int i = 0; i < num; ++i) - { - pos = memberValue(pos); - elements[i] = memberValue; - } - - amv.setValue(elements); - memberValue = amv; - return pos; - } - } +public class AnnotationsAttribute extends AttributeInfo { + /** + * The name of the <code>RuntimeVisibleAnnotations</code> attribute. + */ + public static final String visibleTag = "RuntimeVisibleAnnotations"; + + /** + * The name of the <code>RuntimeInvisibleAnnotations</code> attribute. + */ + public static final String invisibleTag = "RuntimeInvisibleAnnotations"; + + /** + * Constructs a <code>Runtime(In)VisisbleAnnotations_attribute</code>. + * + * @param cp constant pool + * @param attrname attribute name (<code>visibleTag</code> or + * <code>invisibleTag</code>). + * @param info the contents of this attribute. It does not + * include <code>attribute_name_index</code> or + * <code>attribute_length</code>. + */ + public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { + super(cp, attrname, info); + } + + /** + * Constructs an empty + * <code>Runtime(In)VisisbleAnnotations_attribute</code>. + * A new annotation can be later added to the created attribute + * by <code>setAnnotations()</code>. + * + * @param cp constant pool + * @param attrname attribute name (<code>visibleTag</code> or + * <code>invisibleTag</code>). + * @see #setAnnotations(Annotation[]) + */ + public AnnotationsAttribute(ConstPool cp, String attrname) { + this(cp, attrname, new byte[] { 0, 0 }); + } + + /** + * @param n the attribute name. + */ + AnnotationsAttribute(ConstPool cp, int n, DataInputStream in) + throws IOException + { + super(cp, n, in); + } + + /** + * Returns <code>num_annotations</code>. + */ + public int numAnnotations() { + return ByteArray.readU16bit(info, 0); + } + + /** + * Copies this attribute and returns a new copy. + */ + public AttributeInfo copy(ConstPool newCp, Map classnames) { + Copier copier = new Copier(info, constPool, newCp, classnames); + try { + copier.annotationArray(); + return new AnnotationsAttribute(newCp, getName(), copier.close()); + } + catch (Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Parses the annotations and returns a data structure representing + * the annotation with the specified type. See also + * <code>getAnnotations()</code> as to the returned data structure. + * + * @param type the annotation type. + * @see #getAnnotations() + */ + public Annotation getAnnotation(String type) { + Annotation[] annotations = getAnnotations(); + for (int i = 0; i < annotations.length; i++) { + if (annotations[i].getTypeName().equals(type)) + return annotations[i]; + } + + return null; + } + + /** + * Adds an annotation. If there is an annotation with the same type, + * it is removed before the new annotation is added. + * + * @param annotation the added annotation. + */ + public void addAnnotation(Annotation annotation) { + String type = annotation.getTypeName(); + Annotation[] annotations = getAnnotations(); + for (int i = 0; i < annotations.length; i++) { + if (annotations[i].getTypeName().equals(type)) { + annotations[i] = annotation; + setAnnotations(annotations); + return; + } + } + + Annotation[] newlist = new Annotation[annotations.length + 1]; + System.arraycopy(annotations, 0, newlist, 0, annotations.length); + newlist[annotations.length] = annotation; + setAnnotations(newlist); + } + + /** + * Parses the annotations and returns a data structure representing + * that parsed annotations. Note that changes of the node values of the + * returned tree are not reflected on the annotations represented by + * this object unless the tree is copied back to this object by + * <code>setAnnotations()</code>. + * + * @see #setAnnotations(Annotation[]) + */ + public Annotation[] getAnnotations() { + try { + return new Parser(info, constPool).parseAnnotations(); + } + catch (Exception e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Changes the annotations represented by this object according to + * the given array of <code>Annotation</code> objects. + * + * @param annotations the data structure representing the + * new annotations. + */ + public void setAnnotations(Annotation[] annotations) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + AnnotationsWriter writer = new AnnotationsWriter(output, constPool); + try { + int n = annotations.length; + writer.numAnnotations(n); + for (int i = 0; i < n; ++i) + annotations[i].write(writer); + + writer.close(); + } + catch (IOException e) { + throw new RuntimeException(e); // should never reach here. + } + + set(output.toByteArray()); + } + + /** + * Changes the annotations. A call to this method is equivalent to: + * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul> + * + * @param annotation the data structure representing + * the new annotation. + */ + public void setAnnotation(Annotation annotation) { + setAnnotations(new Annotation[] { annotation }); + } + + /** + * Returns a string representation of this object. + */ + public String toString() { + Annotation[] a = getAnnotations(); + StringBuffer sbuf = new StringBuffer(); + int i = 0; + while (i < a.length) { + sbuf.append(a[i++].toString()); + if (i != a.length) + sbuf.append(", "); + } + + return sbuf.toString(); + } + + static class Walker { + byte[] info; + + Walker(byte[] attrInfo) { + info = attrInfo; + } + + final void parameters() throws Exception { + int numParam = info[0] & 0xff; + parameters(numParam, 1); + } + + void parameters(int numParam, int pos) throws Exception { + for (int i = 0; i < numParam; ++i) + pos = annotationArray(pos); + } + + final void annotationArray() throws Exception { + annotationArray(0); + } + + final int annotationArray(int pos) throws Exception { + int num = ByteArray.readU16bit(info, pos); + return annotationArray(pos + 2, num); + } + + int annotationArray(int pos, int num) throws Exception { + for (int i = 0; i < num; ++i) + pos = annotation(pos); + + return pos; + } + + final int annotation(int pos) throws Exception { + int type = ByteArray.readU16bit(info, pos); + int numPairs = ByteArray.readU16bit(info, pos + 2); + return annotation(pos + 4, type, numPairs); + } + + int annotation(int pos, int type, int numPairs) throws Exception { + for (int j = 0; j < numPairs; ++j) + pos = memberValuePair(pos); + + return pos; + } + + final int memberValuePair(int pos) throws Exception { + int nameIndex = ByteArray.readU16bit(info, pos); + return memberValuePair(pos + 2, nameIndex); + } + + int memberValuePair(int pos, int nameIndex) throws Exception { + return memberValue(pos); + } + + final int memberValue(int pos) throws Exception { + int tag = info[pos] & 0xff; + if (tag == 'e') { + int typeNameIndex = ByteArray.readU16bit(info, pos + 1); + int constNameIndex = ByteArray.readU16bit(info, pos + 3); + enumMemberValue(typeNameIndex, constNameIndex); + return pos + 5; + } + else if (tag == 'c') { + int index = ByteArray.readU16bit(info, pos + 1); + classMemberValue(index); + return pos + 3; + } + else if (tag == '@') + return annotationMemberValue(pos + 1); + else if (tag == '[') { + int num = ByteArray.readU16bit(info, pos + 1); + return arrayMemberValue(pos + 3, num); + } + else { // primitive types or String. + int index = ByteArray.readU16bit(info, pos + 1); + constValueMember(tag, index); + return pos + 3; + } + } + + void constValueMember(int tag, int index) throws Exception {} + + void enumMemberValue(int typeNameIndex, int constNameIndex) + throws Exception { + } + + void classMemberValue(int index) throws Exception {} + + int annotationMemberValue(int pos) throws Exception { + return annotation(pos); + } + + int arrayMemberValue(int pos, int num) throws Exception { + for (int i = 0; i < num; ++i) { + pos = memberValue(pos); + } + + return pos; + } + } + + static class Copier extends Walker { + ByteArrayOutputStream output; + AnnotationsWriter writer; + ConstPool srcPool, destPool; + Map classnames; + + /** + * Constructs a copier. This copier renames some class names + * into the new names specified by <code>map</code> when it copies + * an annotation attribute. + * + * @param info the source attribute. + * @param src the constant pool of the source class. + * @param dest the constant pool of the destination class. + * @param map pairs of replaced and substituted class names. + * It can be null. + */ + Copier(byte[] info, ConstPool src, ConstPool dest, Map map) { + super(info); + output = new ByteArrayOutputStream(); + writer = new AnnotationsWriter(output, dest); + srcPool = src; + destPool = dest; + classnames = map; + } + + byte[] close() throws IOException { + writer.close(); + return output.toByteArray(); + } + + void parameters(int numParam, int pos) throws Exception { + writer.numParameters(numParam); + super.parameters(numParam, pos); + } + + int annotationArray(int pos, int num) throws Exception { + writer.numAnnotations(num); + return super.annotationArray(pos, num); + } + + int annotation(int pos, int type, int numPairs) throws Exception { + writer.annotation(copy(type), numPairs); + return super.annotation(pos, type, numPairs); + } + + int memberValuePair(int pos, int nameIndex) throws Exception { + writer.memberValuePair(copy(nameIndex)); + return super.memberValuePair(pos, nameIndex); + } + + void constValueMember(int tag, int index) throws Exception { + writer.constValueIndex(tag, copy(index)); + super.constValueMember(tag, index); + } + + void enumMemberValue(int typeNameIndex, int constNameIndex) + throws Exception + { + writer.enumConstValue(copy(typeNameIndex), copy(constNameIndex)); + super.enumMemberValue(typeNameIndex, constNameIndex); + } + + void classMemberValue(int index) throws Exception { + writer.classInfoIndex(copy(index)); + super.classMemberValue(index); + } + + int annotationMemberValue(int pos) throws Exception { + writer.annotationValue(); + return super.annotationMemberValue(pos); + } + + int arrayMemberValue(int pos, int num) throws Exception { + writer.arrayValue(num); + return super.arrayMemberValue(pos, num); + } + + /** + * Copies a constant pool entry into the destination constant pool + * and returns the index of the copied entry. + * + * @param srcIndex the index of the copied entry into the source + * constant pool. + * @return the index of the copied item into the destination + * constant pool. + */ + int copy(int srcIndex) { + return srcPool.copy(srcIndex, destPool, classnames); + } + } + + static class Parser extends Walker { + ConstPool pool; + Annotation[][] allParams; // all parameters + Annotation[] allAnno; // all annotations + Annotation currentAnno; // current annotation + MemberValue memberValue; + + /** + * Constructs a parser. This parser constructs a parse tree of + * the annotations. + * + * @param info the attribute. + * @param src the constant pool. + */ + Parser(byte[] info, ConstPool cp) { + super(info); + pool = cp; + } + + Annotation[][] parseParameters() throws Exception { + parameters(); + return allParams; + } + + Annotation[] parseAnnotations() throws Exception { + annotationArray(); + return allAnno; + } + + void parameters(int numParam, int pos) throws Exception { + Annotation[][] params = new Annotation[numParam][]; + for (int i = 0; i < numParam; ++i) { + pos = annotationArray(pos); + params[i] = allAnno; + } + + allParams = params; + } + + int annotationArray(int pos, int num) throws Exception { + Annotation[] array = new Annotation[num]; + for (int i = 0; i < num; ++i) { + pos = annotation(pos); + array[i] = currentAnno; + } + + allAnno = array; + return pos; + } + + int annotation(int pos, int type, int numPairs) throws Exception { + currentAnno = new Annotation(type, pool); + return super.annotation(pos, type, numPairs); + } + + int memberValuePair(int pos, int nameIndex) throws Exception { + pos = super.memberValuePair(pos, nameIndex); + currentAnno.addMemberValue(nameIndex, memberValue); + return pos; + } + + void constValueMember(int tag, int index) throws Exception { + MemberValue m; + ConstPool cp = pool; + switch (tag) { + case 'B' : + m = new ByteMemberValue(index, cp); + break; + case 'C' : + m = new CharMemberValue(index, cp); + break; + case 'D' : + m = new DoubleMemberValue(index, cp); + break; + case 'F' : + m = new FloatMemberValue(index, cp); + break; + case 'I' : + m = new IntegerMemberValue(index, cp); + break; + case 'J' : + m = new LongMemberValue(index, cp); + break; + case 'S' : + m = new ShortMemberValue(index, cp); + break; + case 'Z' : + m = new BooleanMemberValue(index, cp); + break; + case 's' : + m = new StringMemberValue(index, cp); + break; + default : + throw new RuntimeException("unknown tag:" + tag); + } + + memberValue = m; + super.constValueMember(tag, index); + } + + void enumMemberValue(int typeNameIndex, int constNameIndex) + throws Exception + { + memberValue = new EnumMemberValue(typeNameIndex, + constNameIndex, pool); + super.enumMemberValue(typeNameIndex, constNameIndex); + } + + void classMemberValue(int index) throws Exception { + memberValue = new ClassMemberValue(index, pool); + super.classMemberValue(index); + } + + int annotationMemberValue(int pos) throws Exception { + Annotation anno = currentAnno; + pos = super.annotationMemberValue(pos); + memberValue = new AnnotationMemberValue(currentAnno, pool); + currentAnno = anno; + return pos; + } + + int arrayMemberValue(int pos, int num) throws Exception { + ArrayMemberValue amv = new ArrayMemberValue(pool); + MemberValue[] elements = new MemberValue[num]; + for (int i = 0; i < num; ++i) { + pos = memberValue(pos); + elements[i] = memberValue; + } + + amv.setValue(elements); + memberValue = amv; + return pos; + } + } } diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java index b0744942..2e1e4fed 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -28,395 +28,364 @@ import java.util.Map; * @see javassist.CtMethod#getMethodInfo() * @see javassist.CtConstructor#getMethodInfo() */ -public final class MethodInfo -{ - ConstPool constPool; - int accessFlags; - int name; - int descriptor; - LinkedList attribute; // may be null - - /** - * The name of constructors: <code><init></code>. - */ - public static final String nameInit = "<init>"; - - /** - * The name of class initializer (static initializer): - * <code><clinit></code>. - */ - public static final String nameClinit = "<clinit>"; - - private MethodInfo(ConstPool cp) - { - constPool = cp; - attribute = null; - } - - /** - * Constructs a <code>method_info</code> structure. - * The initial value of <code>access_flags</code> is zero. - * - * @param cp a constant pool table - * @param methodname method name - * @param desc method descriptor - * @see Descriptor - */ - public MethodInfo(ConstPool cp, String methodname, String desc) - { - this(cp); - accessFlags = 0; - name = cp.addUtf8Info(methodname); - descriptor = constPool.addUtf8Info(desc); - } - - MethodInfo(ConstPool cp, DataInputStream in) throws IOException - { - this(cp); - read(in); - } - - /** - * Constructs a copy of <code>method_info</code> structure. - * Class names appearing in the source <code>method_info</code> - * are renamed according to <code>classnameMap</code>. - * <p/> - * <p>Note: only <code>Code</code> and <code>Exceptions</code> - * attributes are copied from the source. The other attributes - * are ignored. - * - * @param cp a constant pool table - * @param methodname a method name - * @param src a source <code>method_info</code> - * @param classnameMap specifies pairs of replaced and substituted - * name. - * @see Descriptor - */ - public MethodInfo(ConstPool cp, String methodname, MethodInfo src, - Map classnameMap) throws BadBytecode - { - this(cp); - read(src, methodname, classnameMap); - } - - void prune(ConstPool cp) - { - new Exception("****** pruned *****").printStackTrace(); - attribute = null; - name = cp.addUtf8Info(getName()); - descriptor = cp.addUtf8Info(getDescriptor()); - constPool = cp; - } - - /** - * Returns a method name. - */ - public String getName() - { - return constPool.getUtf8Info(name); - } - - /** - * Sets a method name. - */ - public void setName(String newName) - { - name = constPool.addUtf8Info(newName); - } - - /** - * Returns true if this is not a constructor or a class initializer - * (static initializer). - */ - public boolean isMethod() - { - String n = getName(); - return !n.equals(nameInit) && !n.equals(nameClinit); - } - - /** - * Returns a constant pool table used by this method. - */ - public ConstPool getConstPool() - { - return constPool; - } - - /** - * Returns true if this is a constructor. - */ - public boolean isConstructor() - { - return getName().equals(nameInit); - } - - /** - * Returns true if this is a class initializer (static initializer). - */ - public boolean isStaticInitializer() - { - return getName().equals(nameClinit); - } - - /** - * Returns access flags. - * - * @see AccessFlag - */ - public int getAccessFlags() - { - return accessFlags; - } - - /** - * Sets access flags. - * - * @see AccessFlag - */ - public void setAccessFlags(int acc) - { - accessFlags = acc; - } - - /** - * Returns a method descriptor. - * - * @see Descriptor - */ - public String getDescriptor() - { - return constPool.getUtf8Info(descriptor); - } - - /** - * Sets a method descriptor. - * - * @see Descriptor - */ - public void setDescriptor(String desc) - { - if (!desc.equals(getDescriptor())) - descriptor = constPool.addUtf8Info(desc); - } - - /** - * Returns all the attributes. - * A new element can be added to the returned list - * and an existing element can be removed from the list. - * - * @return a list of <code>AttributeInfo</code> objects. - * @see AttributeInfo - */ - public List getAttributes() - { - if (attribute == null) - attribute = new LinkedList(); - - return attribute; - } - - /** - * Returns the attribute with the specified name. - * If it is not found, this method returns null. - * - * @param name attribute name - * @return an <code>AttributeInfo</code> object or null. - */ - public AttributeInfo getAttribute(String name) - { - return AttributeInfo.lookup(attribute, name); - } - - /** - * Appends an attribute. If there is already an attribute with - * the same name, the new one substitutes for it. - */ - public void addAttribute(AttributeInfo info) - { - if (attribute == null) - attribute = new LinkedList(); - - AttributeInfo.remove(attribute, info.getName()); - attribute.add(info); - } - - /** - * Returns an Exceptions attribute. - * - * @return an Exceptions attribute - * or null if it is not specified. - */ - public ExceptionsAttribute getExceptionsAttribute() - { - AttributeInfo info - = AttributeInfo.lookup(attribute, ExceptionsAttribute.tag); - return (ExceptionsAttribute) info; - } - - /** - * Returns a Code attribute. - * - * @return a Code attribute - * or null if it is not specified. - */ - public CodeAttribute getCodeAttribute() - { - AttributeInfo info - = AttributeInfo.lookup(attribute, CodeAttribute.tag); - return (CodeAttribute) info; - } - - /** - * Removes an Exception attribute. - */ - public void removeExceptionsAttribute() - { - AttributeInfo.remove(attribute, ExceptionsAttribute.tag); - } - - /** - * Adds an Exception attribute. - * <p/> - * <p>The added attribute must share the same constant pool table - * as this <code>method_info</code> structure. - */ - public void setExceptionsAttribute(ExceptionsAttribute cattr) - { - removeExceptionsAttribute(); - if (attribute == null) - attribute = new LinkedList(); - - attribute.add(cattr); - } - - /** - * Removes a Code attribute. - */ - public void removeCodeAttribute() - { - AttributeInfo.remove(attribute, CodeAttribute.tag); - } - - /** - * Adds a Code attribute. - * <p/> - * <p>The added attribute must share the same constant pool table - * as this <code>method_info</code> structure. - */ - public void setCodeAttribute(CodeAttribute cattr) - { - removeCodeAttribute(); - if (attribute == null) - attribute = new LinkedList(); - - attribute.add(cattr); - } - - /** - * Returns the line number of the source line corresponding to the - * specified bytecode contained in this method. - * - * @param pos the position of the bytecode (>= 0). - * an index into the code array. - * @return -1 if this information is not available. - */ - public int getLineNumber(int pos) - { - CodeAttribute ca = getCodeAttribute(); - if (ca == null) - return -1; - - LineNumberAttribute ainfo = - (LineNumberAttribute) ca.getAttribute(LineNumberAttribute.tag); - if (ainfo == null) - return -1; - - return ainfo.toLineNumber(pos); - } - - /** - * Changes a super constructor called by this constructor. - * <p/> - * <p>This method modifies a call to <code>super()</code>, - * which should be at the - * head of a constructor body, so that a constructor in a different - * super class is called. This method does not change actural - * parameters. Hence the new super class must have a constructor - * with the same signature as the original one. - * <p/> - * <p>This method should be called when the super class - * of the class declaring this method is changed. - * <p/> - * <p>This method does not perform anything unless this - * <code>MethodInfo</code> represents a constructor. - * - * @param superclass the new super class - */ - public void setSuperclass(String superclass) throws BadBytecode - { - if (!isConstructor()) - return; - - CodeAttribute ca = getCodeAttribute(); - byte[] code = ca.getCode(); - CodeIterator iterator = ca.iterator(); - int pos = iterator.skipSuperConstructor(); - if (pos >= 0) - { // not this() - ConstPool cp = constPool; - int mref = ByteArray.readU16bit(code, pos + 1); - int nt = cp.getMethodrefNameAndType(mref); - int sc = cp.addClassInfo(superclass); - int mref2 = cp.addMethodrefInfo(sc, nt); - ByteArray.write16bit(mref2, code, pos + 1); - } - } - - private void read(MethodInfo src, String methodname, Map classnames) - throws BadBytecode - { - ConstPool destCp = constPool; - accessFlags = src.accessFlags; - name = destCp.addUtf8Info(methodname); - - ConstPool srcCp = src.constPool; - String desc = srcCp.getUtf8Info(src.descriptor); - String desc2 = Descriptor.rename(desc, classnames); - descriptor = destCp.addUtf8Info(desc2); - - attribute = new LinkedList(); - ExceptionsAttribute eattr = src.getExceptionsAttribute(); - if (eattr != null) - attribute.add(eattr.copy(destCp, classnames)); - - CodeAttribute cattr = src.getCodeAttribute(); - if (cattr != null) - attribute.add(cattr.copy(destCp, classnames)); - } - - private void read(DataInputStream in) throws IOException - { - accessFlags = in.readUnsignedShort(); - name = in.readUnsignedShort(); - descriptor = in.readUnsignedShort(); - int n = in.readUnsignedShort(); - attribute = new LinkedList(); - for (int i = 0; i < n; ++i) - attribute.add(AttributeInfo.read(constPool, in)); - } - - void write(DataOutputStream out) throws IOException - { - out.writeShort(accessFlags); - out.writeShort(name); - out.writeShort(descriptor); - - if (attribute == null) - out.writeShort(0); - else - { - out.writeShort(attribute.size()); - AttributeInfo.writeAll(attribute, out); - } - } +public final class MethodInfo { + ConstPool constPool; + int accessFlags; + int name; + int descriptor; + LinkedList attribute; // may be null + + /** + * The name of constructors: <code><init></code>. + */ + public static final String nameInit = "<init>"; + + /** + * The name of class initializer (static initializer): + * <code><clinit></code>. + */ + public static final String nameClinit = "<clinit>"; + + private MethodInfo(ConstPool cp) { + constPool = cp; + attribute = null; + } + + /** + * Constructs a <code>method_info</code> structure. + * The initial value of <code>access_flags</code> is zero. + * + * @param cp a constant pool table + * @param methodname method name + * @param desc method descriptor + * @see Descriptor + */ + public MethodInfo(ConstPool cp, String methodname, String desc) { + this(cp); + accessFlags = 0; + name = cp.addUtf8Info(methodname); + descriptor = constPool.addUtf8Info(desc); + } + + MethodInfo(ConstPool cp, DataInputStream in) throws IOException { + this(cp); + read(in); + } + + /** + * Constructs a copy of <code>method_info</code> structure. + * Class names appearing in the source <code>method_info</code> + * are renamed according to <code>classnameMap</code>. + * + * <p>Note: only <code>Code</code> and <code>Exceptions</code> + * attributes are copied from the source. The other attributes + * are ignored. + * + * @param cp a constant pool table + * @param methodname a method name + * @param src a source <code>method_info</code> + * @param classnameMap specifies pairs of replaced and substituted + * name. + * @see Descriptor + */ + public MethodInfo(ConstPool cp, String methodname, MethodInfo src, + Map classnameMap) throws BadBytecode + { + this(cp); + read(src, methodname, classnameMap); + } + + void prune(ConstPool cp) { + attribute = null; + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + constPool = cp; + } + + /** + * Returns a method name. + */ + public String getName() { + return constPool.getUtf8Info(name); + } + + /** + * Sets a method name. + */ + public void setName(String newName) { + name = constPool.addUtf8Info(newName); + } + + /** + * Returns true if this is not a constructor or a class initializer + * (static initializer). + */ + public boolean isMethod() { + String n = getName(); + return !n.equals(nameInit) && !n.equals(nameClinit); + } + + /** + * Returns a constant pool table used by this method. + */ + public ConstPool getConstPool() { + return constPool; + } + + /** + * Returns true if this is a constructor. + */ + public boolean isConstructor() { + return getName().equals(nameInit); + } + + /** + * Returns true if this is a class initializer (static initializer). + */ + public boolean isStaticInitializer() { + return getName().equals(nameClinit); + } + + /** + * Returns access flags. + * + * @see AccessFlag + */ + public int getAccessFlags() { + return accessFlags; + } + + /** + * Sets access flags. + * + * @see AccessFlag + */ + public void setAccessFlags(int acc) { + accessFlags = acc; + } + + /** + * Returns a method descriptor. + * + * @see Descriptor + */ + public String getDescriptor() { + return constPool.getUtf8Info(descriptor); + } + + /** + * Sets a method descriptor. + * + * @see Descriptor + */ + public void setDescriptor(String desc) { + if (!desc.equals(getDescriptor())) + descriptor = constPool.addUtf8Info(desc); + } + + /** + * Returns all the attributes. + * A new element can be added to the returned list + * and an existing element can be removed from the list. + * + * @return a list of <code>AttributeInfo</code> objects. + * @see AttributeInfo + */ + public List getAttributes() { + if (attribute == null) + attribute = new LinkedList(); + + return attribute; + } + + /** + * Returns the attribute with the specified name. + * If it is not found, this method returns null. + * + * @param name attribute name + * @return an <code>AttributeInfo</code> object or null. + */ + public AttributeInfo getAttribute(String name) { + return AttributeInfo.lookup(attribute, name); + } + + /** + * Appends an attribute. If there is already an attribute with + * the same name, the new one substitutes for it. + */ + public void addAttribute(AttributeInfo info) { + if (attribute == null) + attribute = new LinkedList(); + + AttributeInfo.remove(attribute, info.getName()); + attribute.add(info); + } + + /** + * Returns an Exceptions attribute. + * + * @return an Exceptions attribute + * or null if it is not specified. + */ + public ExceptionsAttribute getExceptionsAttribute() { + AttributeInfo info + = AttributeInfo.lookup(attribute, ExceptionsAttribute.tag); + return (ExceptionsAttribute)info; + } + + /** + * Returns a Code attribute. + * + * @return a Code attribute + * or null if it is not specified. + */ + public CodeAttribute getCodeAttribute() { + AttributeInfo info + = AttributeInfo.lookup(attribute, CodeAttribute.tag); + return (CodeAttribute)info; + } + + /** + * Removes an Exception attribute. + */ + public void removeExceptionsAttribute() { + AttributeInfo.remove(attribute, ExceptionsAttribute.tag); + } + + /** + * Adds an Exception attribute. + * + * <p>The added attribute must share the same constant pool table + * as this <code>method_info</code> structure. + */ + public void setExceptionsAttribute(ExceptionsAttribute cattr) { + removeExceptionsAttribute(); + if (attribute == null) + attribute = new LinkedList(); + + attribute.add(cattr); + } + + /** + * Removes a Code attribute. + */ + public void removeCodeAttribute() { + AttributeInfo.remove(attribute, CodeAttribute.tag); + } + + /** + * Adds a Code attribute. + * + * <p>The added attribute must share the same constant pool table + * as this <code>method_info</code> structure. + */ + public void setCodeAttribute(CodeAttribute cattr) { + removeCodeAttribute(); + if (attribute == null) + attribute = new LinkedList(); + + attribute.add(cattr); + } + + /** + * Returns the line number of the source line corresponding to the + * specified bytecode contained in this method. + * + * @param pos the position of the bytecode (>= 0). + * an index into the code array. + * @return -1 if this information is not available. + */ + public int getLineNumber(int pos) { + CodeAttribute ca = getCodeAttribute(); + if (ca == null) + return -1; + + LineNumberAttribute ainfo = + (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); + if (ainfo == null) + return -1; + + return ainfo.toLineNumber(pos); + } + + /** + * Changes a super constructor called by this constructor. + * + * <p>This method modifies a call to <code>super()</code>, + * which should be at the + * head of a constructor body, so that a constructor in a different + * super class is called. This method does not change actural + * parameters. Hence the new super class must have a constructor + * with the same signature as the original one. + * + * <p>This method should be called when the super class + * of the class declaring this method is changed. + * + * <p>This method does not perform anything unless this + * <code>MethodInfo</code> represents a constructor. + * + * @param superclass the new super class + */ + public void setSuperclass(String superclass) throws BadBytecode { + if (!isConstructor()) + return; + + CodeAttribute ca = getCodeAttribute(); + byte[] code = ca.getCode(); + CodeIterator iterator = ca.iterator(); + int pos = iterator.skipSuperConstructor(); + if (pos >= 0) { // not this() + ConstPool cp = constPool; + int mref = ByteArray.readU16bit(code, pos + 1); + int nt = cp.getMethodrefNameAndType(mref); + int sc = cp.addClassInfo(superclass); + int mref2 = cp.addMethodrefInfo(sc, nt); + ByteArray.write16bit(mref2, code, pos + 1); + } + } + + private void read(MethodInfo src, String methodname, Map classnames) + throws BadBytecode + { + ConstPool destCp = constPool; + accessFlags = src.accessFlags; + name = destCp.addUtf8Info(methodname); + + ConstPool srcCp = src.constPool; + String desc = srcCp.getUtf8Info(src.descriptor); + String desc2 = Descriptor.rename(desc, classnames); + descriptor = destCp.addUtf8Info(desc2); + + attribute = new LinkedList(); + ExceptionsAttribute eattr = src.getExceptionsAttribute(); + if (eattr != null) + attribute.add(eattr.copy(destCp, classnames)); + + CodeAttribute cattr = src.getCodeAttribute(); + if (cattr != null) + attribute.add(cattr.copy(destCp, classnames)); + } + + private void read(DataInputStream in) throws IOException { + accessFlags = in.readUnsignedShort(); + name = in.readUnsignedShort(); + descriptor = in.readUnsignedShort(); + int n = in.readUnsignedShort(); + attribute = new LinkedList(); + for (int i = 0; i < n; ++i) + attribute.add(AttributeInfo.read(constPool, in)); + } + + void write(DataOutputStream out) throws IOException { + out.writeShort(accessFlags); + out.writeShort(name); + out.writeShort(descriptor); + + if (attribute == null) + out.writeShort(0); + else { + out.writeShort(attribute.size()); + AttributeInfo.writeAll(attribute, out); + } + } } diff --git a/src/main/javassist/bytecode/annotation/ClassMemberValue.java b/src/main/javassist/bytecode/annotation/ClassMemberValue.java index 09a1075b..0be36b24 100644 --- a/src/main/javassist/bytecode/annotation/ClassMemberValue.java +++ b/src/main/javassist/bytecode/annotation/ClassMemberValue.java @@ -17,7 +17,6 @@ package javassist.bytecode.annotation; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; - import java.io.IOException; /** @@ -26,83 +25,74 @@ import java.io.IOException; * @author <a href="mailto:bill@jboss.org">Bill Burke</a> * @author Shigeru Chiba */ -public class ClassMemberValue extends MemberValue -{ - int valueIndex; +public class ClassMemberValue extends MemberValue { + int valueIndex; - /** - * Constructs a string constant value. The initial value is specified - * by the constant pool entry at the given index. - * - * @param index the index of a CONSTANT_Utf8_info structure. - */ - public ClassMemberValue(int index, ConstPool cp) - { - super('c', cp); - this.valueIndex = index; - } + /** + * Constructs a string constant value. The initial value is specified + * by the constant pool entry at the given index. + * + * @param index the index of a CONSTANT_Utf8_info structure. + */ + public ClassMemberValue(int index, ConstPool cp) { + super('c', cp); + this.valueIndex = index; + } - /** - * Constructs a string constant value. - * - * @param className the initial value. - */ - public ClassMemberValue(String className, ConstPool cp) - { - super('c', cp); - setValue(className); - } + /** + * Constructs a string constant value. + * + * @param className the initial value. + */ + public ClassMemberValue(String className, ConstPool cp) { + super('c', cp); + setValue(className); + } - /** - * Constructs a string constant value. - * The initial value is java.lang.Class. - */ - public ClassMemberValue(ConstPool cp) - { - super('c', cp); - setValue("java.lang.Class"); - } + /** + * Constructs a string constant value. + * The initial value is java.lang.Class. + */ + public ClassMemberValue(ConstPool cp) { + super('c', cp); + setValue("java.lang.Class"); + } - /** - * Obtains the value of the member. - * - * @return fully-qualified class name. - */ - public String getValue() - { - String v = cp.getUtf8Info(valueIndex); - return Descriptor.toClassName(v); - } + /** + * Obtains the value of the member. + * + * @return fully-qualified class name. + */ + public String getValue() { + String v = cp.getUtf8Info(valueIndex); + return Descriptor.toClassName(v); + } - /** - * Sets the value of the member. - * - * @param newClassName fully-qualified class name. - */ - public void setValue(String newClassName) - { - String setTo = Descriptor.of(newClassName); - valueIndex = cp.addUtf8Info(setTo); - } + /** + * Sets the value of the member. + * + * @param newClassName fully-qualified class name. + */ + public void setValue(String newClassName) { + String setTo = Descriptor.of(newClassName); + valueIndex = cp.addUtf8Info(setTo); + } - /** - * Obtains the string representation of this object. - */ - public String toString() - { - return "<" + getValue() + " class>"; - } + /** + * Obtains the string representation of this object. + */ + public String toString() { + return "<" + getValue() + " class>"; + } - void write(AnnotationsWriter writer) throws IOException - { - writer.classInfoIndex(valueIndex); - } + void write(AnnotationsWriter writer) throws IOException { + writer.classInfoIndex(valueIndex); + } - /** - * Accepts a visitor. - */ - public void accept(MemberValueVisitor visitor) - { - visitor.visitClassMemberValue(this); - } + /** + * Accepts a visitor. + */ + public void accept(MemberValueVisitor visitor) { + visitor.visitClassMemberValue(this); + } } |