From 02c99beb983350fa2ae2808ea35c9416cd63db5b Mon Sep 17 00:00:00 2001 From: patriot1burke Date: Tue, 14 Sep 2004 04:17:29 +0000 Subject: [PATCH] * additional methods to AnnotationsAttribute to support adding * turn off pruning. * move toClass back to ClassPool git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@137 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/javassist/ClassPool.java | 1310 +++++----- src/main/javassist/CtClass.java | 2217 ++++++++-------- src/main/javassist/CtClassType.java | 2278 +++++++++-------- .../bytecode/AnnotationsAttribute.java | 1068 ++++---- src/main/javassist/bytecode/MethodInfo.java | 755 +++--- .../bytecode/annotation/ClassMemberValue.java | 132 +- 6 files changed, 4143 insertions(+), 3617 deletions(-) diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index a75dec94..b0a2066c 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -15,7 +15,11 @@ package javassist; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import java.util.Hashtable; @@ -27,9 +31,9 @@ import java.util.Hashtable; * to find a class file and then it creates a CtClass object * representing that class file. The created object is returned to the * caller. - * + *

*

Memory consumption memo: - * + *

*

ClassPool objects hold all the CtClasses * that have been created so that the consistency among modified classes * can be guaranteed. Thus if a large number of CtClasses @@ -39,621 +43,723 @@ import java.util.Hashtable; * Note that getDefault() is a singleton factory. * Otherwise, detach() in CtClass should be used * to avoid huge memory consumption. - * + *

*

ClassPool hierarchy: - * + *

*

ClassPools can make a parent-child hierarchy as * java.lang.ClassLoaders. If a ClassPool has * a parent pool, get() first asks the parent pool to find * a class file. Only if the parent could not find the class file, * get() searches the ClassPaths of * the child ClassPool. This search order is reversed if - * ClassPath.childFirstLookup is true. + * ClassPath.childFirstLookup is true. * * @see javassist.CtClass * @see javassist.ClassPath */ -public class ClassPool { - - /** - * Determines the search order. - * - *

If this field is true, get() first searches the - * class path associated to this ClassPool and then - * the class path associated with the parent ClassPool. - * Otherwise, the class path associated with the parent is searched - * first. - * - *

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 null. - * @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. - * - *

The default class pool searches the system search path, - * which usually includes the platform library, extension - * libraries, and the search path specified by the - * -classpath option or the CLASSPATH - * environment variable. - * - *

When this method is called for the first time, the default - * class pool is created with the following code snippet: - * - *

- * - *

If the default class pool cannot find any class files, - * try ClassClassPath and LoaderClassPath. - * - * @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 get() 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 $cflow variable for the field specified - * by cname and fname. - * - * @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 $cflow variable - */ - public Object[] lookupCflow(String name) { - if (cflow == null) - cflow = new Hashtable(); - - return (Object[])cflow.get(name); - } - - /** - * Reads a class file and constructs a CtClass - * 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, - * - *

- * - * returns a CtClass object representing Pair - * class. The definition of Pair is the same as that of - * Point class except the class name since Pair - * is defined by reading Point.class. - * - * @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 CtClass - * object representing that class file. If that class file has been - * already read, this method returns a reference to the - * CtClass created when that class file was read at the - * first time. - * - *

If classname ends with "[]", then this method - * returns a CtClass object for that array type. - * - *

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; - } +public class ClassPool +{ - if (!childFirstLookup && parent != null) { - clazz = parent.get0(classname, useCache); - if (clazz != null) - return clazz; - } + /** + * Determines the search order. + *

+ *

If this field is true, get() first searches the + * class path associated to this ClassPool and then + * the class path associated with the parent ClassPool. + * Otherwise, the class path associated with the parent is searched + * first. + *

+ *

The default value is false. + */ + public boolean childFirstLookup = false; + + protected ClassPoolTail source; + protected ClassPool parent; + protected Hashtable classes; // should be synchronous - clazz = createCtClass(classname, useCache); - if (clazz != null) { - if (useCache) - cacheCtClass(classname, clazz); + /** + * 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 null. + * @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. + *

+ *

The default class pool searches the system search path, + * which usually includes the platform library, extension + * libraries, and the search path specified by the + * -classpath option or the CLASSPATH + * environment variable. + *

+ *

When this method is called for the first time, the default + * class pool is created with the following code snippet: + *

+ *

+ *

+ *

If the default class pool cannot find any class files, + * try ClassClassPath and LoaderClassPath. + * + * @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 get() 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 $cflow variable for the field specified + * by cname and fname. + * + * @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 $cflow variable + */ + public Object[] lookupCflow(String name) + { + if (cflow == null) + cflow = new Hashtable(); + + return (Object[]) cflow.get(name); + } + + /** + * Reads a class file and constructs a CtClass + * 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, + *

+ *

+ *

+ * returns a CtClass object representing Pair + * class. The definition of Pair is the same as that of + * Point class except the class name since Pair + * is defined by reading Point.class. + * + * @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 CtClass + * object representing that class file. If that class file has been + * already read, this method returns a reference to the + * CtClass created when that class file was read at the + * first time. + *

+ *

If classname ends with "[]", then this method + * returns a CtClass object for that array type. + *

+ *

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); - - 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."); - } - } - 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 - * CtClass - * objects representing those class files. - * - *

If an element of classnames ends with "[]", - * then this method - * returns a CtClass 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. - * - *

This method is used for creating a CtClass 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. - * @exception 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. - * @exception 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. - * @exception 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. - * @exception 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. - * @exception 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 - * -classpath option or the CLASSPATH - * environment variable. - * - * @return the appended class path. - */ - public ClassPath appendSystemPath() { - return source.appendSystemPath(); - } - - /** - * Insert a ClassPath 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 ClassPath 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. - * @exception 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. - * @exception NotFoundException if the jar file is not found. - */ - public ClassPath appendClassPath(String pathname) - throws NotFoundException - { - return source.appendClassPath(pathname); - } - - /** - * Detatches the ClassPath object from the search path. - * The detached ClassPath object cannot be added - * to the pathagain. - */ - public void removeClassPath(ClassPath cp) { - source.removeClassPath(cp); - } - - /** - * Appends directories and jar files for search. - * - *

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 ("/"). - * - * @exception 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 (!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); } - else { - appendClassPath(pathlist.substring(i, j)); - i = j + 1; + catch (NotFoundException e) + { } - } - } + 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 + * CtClass + * objects representing those class files. + *

+ *

If an element of classnames ends with "[]", + * then this method + * returns a CtClass 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. + *

+ *

This method is used for creating a CtClass 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 + * -classpath option or the CLASSPATH + * environment variable. + * + * @return the appended class path. + */ + public ClassPath appendSystemPath() + { + return source.appendSystemPath(); + } + + /** + * Insert a ClassPath 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 ClassPath 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 ClassPath object from the search path. + * The detached ClassPath object cannot be added + * to the pathagain. + */ + public void removeClassPath(ClassPath cp) + { + source.removeClassPath(cp); + } + + /** + * Appends directories and jar files for search. + *

+ *

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 java.lang.Class 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. + *

+ *

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 java.lang.Class object. + * Once this method is called, further modifications are not allowed + * any more. + *

+ *

The class file represented by this CtClass is + * loaded by the given class loader to construct a + * java.lang.Class object. Since a private method + * on the class loader is invoked through the reflection API, + * the caller must have permissions to do that. + *

+ *

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 0e435d78..5a1805cc 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -15,11 +15,20 @@ package javassist; -import java.io.*; -import javassist.bytecode.*; -import java.util.Collection; +import javassist.bytecode.ClassFile; +import javassist.bytecode.Descriptor; +import javassist.bytecode.Opcode; import javassist.expr.ExprEditor; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.net.URL; +import java.util.Collection; // Subclasses of CtClass: CtClassType, CtPrimitiveType, and CtArray @@ -29,1069 +38,1141 @@ import java.net.URL; * * @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. - * - *

The following command invokes this method: - * - *

- */ - 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 CtClass object representing - * the boolean type. - */ - public static CtClass booleanType; - - /** - * The CtClass object representing - * the char type. - */ - public static CtClass charType; - - /** - * The CtClass object representing - * the byte type. - */ - public static CtClass byteType; - - /** - * The CtClass object representing - * the short type. - */ - public static CtClass shortType; - - /** - * The CtClass object representing - * the int type. - */ - public static CtClass intType; - - /** - * The CtClass object representing - * the long type. - */ - public static CtClass longType; - - /** - * The CtClass object representing - * the float type. - */ - public static CtClass floatType; - - /** - * The CtClass object representing - * the double type. - */ - public static CtClass doubleType; - - /** - * The CtClass object representing - * the void 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 ClassPool for this class. - */ - public ClassPool getClassPool() { return null; } - - /** - * Returns a class file for this class. - * - *

This method is not available if isFrozen() - * 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. - * - *

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. - * - *

If defrost() 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 CtClass object is converted into a class file. - * Pruning saves memory space since a ClassPool holds - * all instances of CtClass - * 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, toBytecode(), - * writeFile(), or toClass() cannot - * be called again. - * - *

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 true if this object represents a primitive - * Java type: boolean, byte, char, short, int, long, float, double, - * or void. - */ - public boolean isPrimitive() { return false; } - - /** - * Returns true 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 null. - */ - public CtClass getComponentType() throws NotFoundException { - return null; - } - - /** - * Returns true if this class extends or implements - * clazz. It also returns true if - * this class is the same as clazz. - */ - 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 null. - */ - 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 newName for all occurrences of a class - * name oldName 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 map. - * - *

All the class names appearing in the class file are tested - * with map 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 ClassMap so that get() - * 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. - * - *

This method may return null. - */ - 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 true if this object represents an interface. - */ - public boolean isInterface() { - return false; - } - - /** - * Returns the modifiers for this class, encoded in an integer. - * For decoding, use javassist.Modifier. - * - * @see Modifier - */ - public int getModifiers() { - return 0; - } - - /** - * Sets the modifiers. - * - * @param mod modifiers encoded by - * javassist.Modifier - * @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. - * - *

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 - * java.lang.Object class and thus it does not have - * the super class. - * - *

If this object represents an interface, this method - * always returns the java.lang.Object class. - * To obtain the super interfaces - * extended by that interface, call getInterfaces(). - */ - 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. - * - *

If this object represents an interface, this method is equivalent - * to addInterface(); it appends clazz 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 CtClass objects - * representing interfaces, or - * null 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 CtClass. - * - *

The current implementation only supports a static nested class. - * isStatic 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 CtField 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. - * - *

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. - * - *

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 CtConstructor 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 javassist.bytecode.Descriptor. - * - * @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 null if - * no class initializer is not declared. - * - * @see #makeClassInitializer() - * @see javassist.CtConstructor - */ - public CtConstructor getClassInitializer() { - return null; - } - - /** - * Returns an array containing CtMethod 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. - * - *

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. - * - *

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 makeClassInitializer(). - * - * @see #makeClassInitializer() - */ - public void addConstructor(CtConstructor c) - throws CannotCompileException - { - checkModify(); - } - - /** - * Adds a method. - */ - public void addMethod(CtMethod m) throws CannotCompileException { - checkModify(); - } - - /** - * Adds a field. - * - *

The CtField belonging to another - * CtClass 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. - * - *

The CtField belonging to another - * CtClass cannot be directly added to this class. - * Only a field created for this class can be added. - * - *

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. - * - *

- * - *

Here, the type of variable cc is CtClass. - * The type of f is CtField. - * - * @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. - * - *

The CtField belonging to another - * CtClass cannot be directly added to this class. - * Only a field created for this class can be added. - * - *

For example, - * - *

- * - *

This code adds an int 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. - * - *

This is a convenient method mainly for obtaining - * a user-defined attribute. For dealing with attributes, see the - * javassist.bytecode package. For example, the following - * expression returns all the attributes of a class file. - * - *

- * - * @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. - * - *

This is a convenient method mainly for adding - * a user-defined attribute. For dealing with attributes, see the - * javassist.bytecode package. For example, the following - * expression adds an attribute of a class file. - * - *

- * - * @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 instrument() - * on every CtMethod and CtConstructor 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 instrument() - * on every CtMethod and CtConstructor object - * in the class. - * - * @param editor specifies how to modify. - */ - public void instrument(ExprEditor editor) - throws CannotCompileException - { - checkModify(); - } - - /** - * Converts this class to a java.lang.Class 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. - * - *

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 toClass(Thread.currentThread().getContextClassLoader()); - } - - /** - * Converts this class to a java.lang.Class object. - * Once this method is called, further modifications are not allowed - * any more. - * - *

The class file represented by this CtClass is - * loaded by the given class loader to construct a - * java.lang.Class object. Since a private method - * on the class loader is invoked through the reflection API, - * the caller must have permissions to do that. - * - *

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 - { - try { - byte[] b = 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[] { 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); - } - } - - /** - * Removes this CtClass object from the - * ClassPool. - * After this method is called, any method cannot be called on the - * removed CtClass object. - * - *

If get() in ClassPool is called - * with the name of the removed method, - * the ClassPool will read the class file again - * and constructs another CtClass 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 CtClass - * 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 CtClass - * 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. - * - *

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. + *

+ *

The following command invokes this method: + *

+ *

+ */ + 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 CtClass object representing + * the boolean type. + */ + public static CtClass booleanType; + + /** + * The CtClass object representing + * the char type. + */ + public static CtClass charType; + + /** + * The CtClass object representing + * the byte type. + */ + public static CtClass byteType; + + /** + * The CtClass object representing + * the short type. + */ + public static CtClass shortType; + + /** + * The CtClass object representing + * the int type. + */ + public static CtClass intType; + + /** + * The CtClass object representing + * the long type. + */ + public static CtClass longType; + + /** + * The CtClass object representing + * the float type. + */ + public static CtClass floatType; + + /** + * The CtClass object representing + * the double type. + */ + public static CtClass doubleType; + + /** + * The CtClass object representing + * the void 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 ClassPool for this class. + */ + public ClassPool getClassPool() + { + return null; + } + + /** + * Returns a class file for this class. + *

+ *

This method is not available if isFrozen() + * 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. + *

+ *

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. + *

+ *

If defrost() 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 CtClass object is converted into a class file. + * Pruning saves memory space since a ClassPool holds + * all instances of CtClass + * 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, toBytecode(), + * writeFile(), or toClass() cannot + * be called again. + *

+ *

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 true if this object represents a primitive + * Java type: boolean, byte, char, short, int, long, float, double, + * or void. + */ + public boolean isPrimitive() + { + return false; + } + + /** + * Returns true 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 null. + */ + public CtClass getComponentType() throws NotFoundException + { + return null; + } + + /** + * Returns true if this class extends or implements + * clazz. It also returns true if + * this class is the same as clazz. + */ + 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 null. + */ + 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 newName for all occurrences of a class + * name oldName 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 map. + *

+ *

All the class names appearing in the class file are tested + * with map 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 ClassMap so that get() + * 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. + *

+ *

This method may return null. + */ + 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 true if this object represents an interface. + */ + public boolean isInterface() + { + return false; + } + + /** + * Returns the modifiers for this class, encoded in an integer. + * For decoding, use javassist.Modifier. + * + * @see Modifier + */ + public int getModifiers() + { + return 0; + } + + /** + * Sets the modifiers. + * + * @param mod modifiers encoded by + * javassist.Modifier + * @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. + *

+ *

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 + * java.lang.Object class and thus it does not have + * the super class. + *

+ *

If this object represents an interface, this method + * always returns the java.lang.Object class. + * To obtain the super interfaces + * extended by that interface, call getInterfaces(). + */ + 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. + *

+ *

If this object represents an interface, this method is equivalent + * to addInterface(); it appends clazz 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 CtClass objects + * representing interfaces, or + * null 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 CtClass. + *

+ *

The current implementation only supports a static nested class. + * isStatic 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 CtField 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. + *

+ *

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. + *

+ *

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 CtConstructor 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 javassist.bytecode.Descriptor. + * + * @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 null if + * no class initializer is not declared. + * + * @see #makeClassInitializer() + * @see javassist.CtConstructor + */ + public CtConstructor getClassInitializer() + { + return null; + } + + /** + * Returns an array containing CtMethod 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. + *

+ *

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. + *

+ *

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 makeClassInitializer(). + * + * @see #makeClassInitializer() + */ + public void addConstructor(CtConstructor c) + throws CannotCompileException + { + checkModify(); + } + + /** + * Adds a method. + */ + public void addMethod(CtMethod m) throws CannotCompileException + { + checkModify(); + } + + /** + * Adds a field. + *

+ *

The CtField belonging to another + * CtClass 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. + *

+ *

The CtField belonging to another + * CtClass cannot be directly added to this class. + * Only a field created for this class can be added. + *

+ *

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. + *

+ *

+ *

+ *

Here, the type of variable cc is CtClass. + * The type of f is CtField. + * + * @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. + *

+ *

The CtField belonging to another + * CtClass cannot be directly added to this class. + * Only a field created for this class can be added. + *

+ *

For example, + *

+ *

+ *

+ *

This code adds an int 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. + *

+ *

This is a convenient method mainly for obtaining + * a user-defined attribute. For dealing with attributes, see the + * javassist.bytecode package. For example, the following + * expression returns all the attributes of a class file. + *

+ *

+ * + * @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. + *

+ *

This is a convenient method mainly for adding + * a user-defined attribute. For dealing with attributes, see the + * javassist.bytecode package. For example, the following + * expression adds an attribute of a class file. + *

+ *

+ * + * @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 instrument() + * on every CtMethod and CtConstructor 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 instrument() + * on every CtMethod and CtConstructor object + * in the class. + * + * @param editor specifies how to modify. + */ + public void instrument(ExprEditor editor) + throws CannotCompileException + { + checkModify(); + } + + /** + * Converts this class to a java.lang.Class 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. + *

+ *

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 java.lang.Class object. + * Once this method is called, further modifications are not allowed + * any more. + *

+ *

The class file represented by this CtClass is + * loaded by the given class loader to construct a + * java.lang.Class object. Since a private method + * on the class loader is invoked through the reflection API, + * the caller must have permissions to do that. + *

+ *

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 CtClass object from the + * ClassPool. + * After this method is called, any method cannot be called on the + * removed CtClass object. + *

+ *

If get() in ClassPool is called + * with the name of the removed method, + * the ClassPool will read the class file again + * and constructs another CtClass 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 CtClass + * 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 CtClass + * 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. + *

+ *

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 cda8fd36..177f772d 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -15,1087 +15,1267 @@ package javassist; -import javassist.bytecode.*; -import javassist.compiler.Javac; -import javassist.compiler.CompileError; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.AttributeInfo; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.Bytecode; +import javassist.bytecode.ClassFile; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; +import javassist.bytecode.InnerClassesAttribute; +import javassist.bytecode.MethodInfo; import javassist.compiler.AccessorMaker; +import javassist.compiler.CompileError; +import javassist.compiler.Javac; import javassist.expr.ExprEditor; -import java.io.InputStream; + import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; -import java.util.Enumeration; 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 = true; - int getCounter; - private static int readCounter = 0; - private static final int READ_THRESHOLD = 100; // see getClassFile2() - - CtClassType(String name, ClassPool cp) { - super(name); - classPool = cp; - wasChanged = wasFrozen = 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); +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(", "); - 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) + } + } + 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()); - 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; + 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; + } - ClassFile file = getClassFile2(); - String supername = file.getSuperclass(); - if (supername != null && supername.equals(cname)) + 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)) return true; - String[] ifs = file.getInterfaces(); - int num = ifs.length; - for (i = 0; i < num; ++i) - if (ifs[i].equals(cname)) - return true; + if (supername != null && classPool.get(supername).subtypeOf(clazz)) + return true; - if (supername != null && classPool.get(supername).subtypeOf(clazz)) + for (i = 0; i < num; ++i) + if (classPool.get(ifs[i]).subtypeOf(clazz)) return true; - 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; - } + 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); - 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); - - 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) + } + } + } + + 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; - } - - 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; - } + } + } + + 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; - 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(), "", "()V"); - m.setAccessFlags(AccessFlag.STATIC); - m.setCodeAttribute(code.toCodeAttribute()); - cf.addMethod(m); - } - else { - CodeAttribute codeAttr = m.getCodeAttribute(); - if (codeAttr == null) - throw new CannotCompileException("empty "); - - 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); - } + 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; } - } - } - - 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) { + } + 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(), "", "()V"); + m.setAccessFlags(AccessFlag.STATIC); + m.setCodeAttribute(code.toCodeAttribute()); + cf.addMethod(m); + } + else + { + CodeAttribute codeAttr = m.getCodeAttribute(); + if (codeAttr == null) + throw new CannotCompileException("empty "); + + 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); - } - - 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++; } + } + } + } + + 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++; + } } -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 a6dfe87a..2483c01b 100644 --- a/src/main/javassist/bytecode/AnnotationsAttribute.java +++ b/src/main/javassist/bytecode/AnnotationsAttribute.java @@ -15,30 +15,46 @@ package javassist.bytecode; -import java.util.Map; -import java.io.IOException; -import java.io.DataInputStream; -import java.io.ByteArrayOutputStream; +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 javassist.bytecode.annotation.*; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; /** * A class representing * RuntimeVisibleAnnotations_attribute and * RuntimeInvisibleAnnotations_attribute. - * + *

*

To obtain an AnnotationAttribute object, invoke * getAttribute(AnnotationsAttribute.invisibleTag) * in ClassFile, MethodInfo, * or FieldInfo. The obtained attribute is a - * runtime invisible annotations attribute. + * runtime invisible annotations attribute. * If the parameter is * AnnotationAttribute.visibleTag, then the obtained * attribute is a runtime visible one. - * + *

*

If you want to record a new AnnotationAttribute object, execute the * following snippet: - * + *

*

*/ -public class AnnotationsAttribute extends AttributeInfo { - /** - * The name of the RuntimeVisibleAnnotations attribute. - */ - public static final String visibleTag = "RuntimeVisibleAnnotations"; - - /** - * The name of the RuntimeInvisibleAnnotations attribute. - */ - public static final String invisibleTag = "RuntimeInvisibleAnnotations"; - - /** - * Constructs a Runtime(In)VisisbleAnnotations_attribute. - * - * @param cp constant pool - * @param attrname attribute name (visibleTag or - * invisibleTag). - * @param info the contents of this attribute. It does not - * include attribute_name_index or - * attribute_length. - */ - public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { - super(cp, attrname, info); - } - - /** - * Constructs an empty - * Runtime(In)VisisbleAnnotations_attribute. - * A new annotation can be later added to the created attribute - * by setAnnotations(). - * - * @param cp constant pool - * @param attrname attribute name (visibleTag or - * invisibleTag). - * @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 num_annotations. - */ - 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 - * 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 - * setAnnotations(). - * - * @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 Annotation 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: - * - * - * @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 map 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 RuntimeVisibleAnnotations attribute. + */ + public static final String visibleTag = "RuntimeVisibleAnnotations"; + + /** + * The name of the RuntimeInvisibleAnnotations attribute. + */ + public static final String invisibleTag = "RuntimeInvisibleAnnotations"; + + private HashMap annotationMap; + + /** + * Constructs a Runtime(In)VisisbleAnnotations_attribute. + * + * @param cp constant pool + * @param attrname attribute name (visibleTag or + * invisibleTag). + * @param info the contents of this attribute. It does not + * include attribute_name_index or + * attribute_length. + */ + public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) + { + super(cp, attrname, info); + } + + /** + * Constructs an empty + * Runtime(In)VisisbleAnnotations_attribute. + * A new annotation can be later added to the created attribute + * by setAnnotations(). + * + * @param cp constant pool + * @param attrname attribute name (visibleTag or + * invisibleTag). + * @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 num_annotations. + */ + 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 + * setAnnotations(). + * + * @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 Annotation 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: + * + * + * @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 map 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 9e866578..b0744942 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -18,9 +18,9 @@ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.Map; -import java.util.List; import java.util.LinkedList; +import java.util.List; +import java.util.Map; /** * method_info structure. @@ -28,364 +28,395 @@ import java.util.LinkedList; * @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: <init>. - */ - public static final String nameInit = ""; - - /** - * The name of class initializer (static initializer): - * <clinit>. - */ - public static final String nameClinit = ""; - - private MethodInfo(ConstPool cp) { - constPool = cp; - attribute = null; - } - - /** - * Constructs a method_info structure. - * The initial value of access_flags 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 method_info structure. - * Class names appearing in the source method_info - * are renamed according to classnameMap. - * - *

Note: only Code and Exceptions - * 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 method_info - * @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 AttributeInfo 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 AttributeInfo 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. - * - *

The added attribute must share the same constant pool table - * as this method_info 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. - * - *

The added attribute must share the same constant pool table - * as this method_info 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. - * - *

This method modifies a call to super(), - * 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. - * - *

This method should be called when the super class - * of the class declaring this method is changed. - * - *

This method does not perform anything unless this - * MethodInfo 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: <init>. + */ + public static final String nameInit = ""; + + /** + * The name of class initializer (static initializer): + * <clinit>. + */ + public static final String nameClinit = ""; + + private MethodInfo(ConstPool cp) + { + constPool = cp; + attribute = null; + } + + /** + * Constructs a method_info structure. + * The initial value of access_flags 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 method_info structure. + * Class names appearing in the source method_info + * are renamed according to classnameMap. + *

+ *

Note: only Code and Exceptions + * 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 method_info + * @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 AttributeInfo 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 AttributeInfo 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. + *

+ *

The added attribute must share the same constant pool table + * as this method_info 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. + *

+ *

The added attribute must share the same constant pool table + * as this method_info 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. + *

+ *

This method modifies a call to super(), + * 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. + *

+ *

This method should be called when the super class + * of the class declaring this method is changed. + *

+ *

This method does not perform anything unless this + * MethodInfo 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 78a6217d..09a1075b 100644 --- a/src/main/javassist/bytecode/annotation/ClassMemberValue.java +++ b/src/main/javassist/bytecode/annotation/ClassMemberValue.java @@ -17,6 +17,7 @@ package javassist.bytecode.annotation; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; + import java.io.IOException; /** @@ -25,72 +26,83 @@ import java.io.IOException; * @author Bill Burke * @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() { - return Descriptor.toClassName(cp.getUtf8Info(valueIndex)); - } + /** + * 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) { - valueIndex = cp.addUtf8Info(Descriptor.of(newClassName)); - } + /** + * 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.constValueIndex(getValue()); - } + 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); + } } -- 2.39.5