diff options
-rw-r--r-- | Readme.html | 9 | ||||
-rw-r--r-- | build.xml | 79 | ||||
-rw-r--r-- | sample/Test.java | 2 | ||||
-rw-r--r-- | sample/evolve/DemoLoader.java | 13 | ||||
-rw-r--r-- | src/main/javassist/AbsClassPool.java | 47 | ||||
-rw-r--r-- | src/main/javassist/ClassPath.java | 2 | ||||
-rw-r--r-- | src/main/javassist/ClassPool.java | 321 | ||||
-rw-r--r-- | src/main/javassist/ClassPoolTail.java | 62 | ||||
-rw-r--r-- | src/main/javassist/CtClassType.java | 8 | ||||
-rw-r--r-- | src/main/javassist/Translator.java | 2 | ||||
-rw-r--r-- | src/main/javassist/bytecode/ClassFile.java | 1212 | ||||
-rw-r--r-- | src/main/javassist/bytecode/FieldInfo.java | 431 | ||||
-rw-r--r-- | src/main/javassist/bytecode/MethodInfo.java | 850 | ||||
-rw-r--r-- | src/main/javassist/reflect/Compiler.java | 3 | ||||
-rw-r--r-- | src/main/javassist/reflect/Loader.java | 4 | ||||
-rw-r--r-- | src/main/javassist/rmi/AppletServer.java | 9 |
16 files changed, 1568 insertions, 1486 deletions
diff --git a/Readme.html b/Readme.html index 3d688faa..c5d0b579 100644 --- a/Readme.html +++ b/Readme.html @@ -67,7 +67,12 @@ other editors. <h2>How to run sample programs</h2> -<p>JDK 1.2.2 or later is needed. +<p>JDK 1.3 or later is needed. + +<h3>0. If you have Apache Ant</h3> + +<p>Run the sample-all task. +Otherwise, follow the instructions below. <h3>1. Move to the directory where this Readme.html file is located.</h3> @@ -253,6 +258,8 @@ see javassist.Dump. <p>- version 2.7 <ul> + <li>javassist.bytecode.annotation has been added. + <li>Now local variables were made available in the source text passed to CtBehavior.insertBefore(), MethodCall.replace(), etc. <li>CtClass.main(), which prints the version number, has been added. @@ -1,18 +1,21 @@ <?xml version="1.0"?>
-<!-- ======================================================================= -->
-<!-- JBoss build file -->
-<!-- ======================================================================= -->
+<!-- =================================================================== -->
+<!-- JBoss build file -->
+<!-- =================================================================== -->
<project name="javassist" default="jar" basedir=".">
<property name="dist-version" value="javassist-2.7"/>
<property environment="env"/>
+ <property name="target.jar" value="javassist.jar"/>
<property name="src.dir" value="${basedir}/src/main"/>
<property name="build.dir" value="${basedir}/build"/>
<property name="build.classes.dir" value="${build.dir}/classes"/>
+ <property name="run.dir" value="${build.classes.dir}"/>
+
<!-- Build classpath -->
<path id="classpath">
<pathelement location="${build.classes.dir}"/>
@@ -61,7 +64,7 @@ to ${build.classes.dir}.</echo> </target>
<target name="jar" depends="compile">
- <jar jarfile="javassist.jar" manifest="${src.dir}/META-INF/MANIFEST.MF">
+ <jar jarfile="${target.jar}" manifest="${src.dir}/META-INF/MANIFEST.MF">
<fileset dir="${build.classes.dir}">
<include name="**/*.class"/>
</fileset>
@@ -103,8 +106,72 @@ Copyright (C) 1999-2004 Shigeru Chiba. All Rights Reserved.</i>]]></bottom> <target name="clean">
<delete dir="build"/>
<delete dir="html"/>
- <delete file="javassist.jar"/>
+ <delete file="${target.jar}"/>
<delete file="${dist-version}.zip"/>
</target>
-</project>
+ <!-- =================================================================== -->
+ <!-- Run samples -->
+ <!-- =================================================================== -->
+
+ <target name = "sample-all"
+ depends="sample-test,sample-reflect,sample-duplicate,sample-vector">
+ <echo>** please run sample-rmi and sample-evolve separately **</echo>
+ </target>
+
+ <target name = "sample-test" depends="sample" >
+ <java fork="true" dir="${run.dir}" classname="sample.Test">
+ <classpath refid="classpath"/>
+ </java>
+ </target>
+
+ <target name = "sample-reflect" depends="sample" >
+ <java fork="true" dir="${run.dir}" classname="javassist.reflect.Loader">
+ <classpath refid="classpath"/>
+ <arg line="sample.reflect.Main Joe" />
+ </java>
+ </target>
+
+ <target name = "sample-duplicate" depends="sample" >
+ <echo>run sample.duplicate.Viewer without reflection</echo>
+ <java fork="true" dir="${run.dir}" classname="sample.duplicate.Viewer">
+ <classpath refid="classpath"/>
+ </java>
+ <echo>run sample.duplicate.Viewer with reflection</echo>
+ <java fork="true" dir="${run.dir}" classname="sample.duplicate.Main">
+ <classpath refid="classpath"/>
+ </java>
+ </target>
+
+ <target name = "sample-vector" depends="sample" >
+ <echo>javassist.preproc.Compiler sample/vector/Test.j</echo>
+ <java fork="true" dir="${run.dir}" classname="javassist.preproc.Compiler">
+ <classpath refid="classpath"/>
+ <arg line="sample/vector/Test.j"/>
+ </java>
+ <echo>javac sample/vector/Test.java</echo>
+ <javac srcdir="${build.classes.dir}"
+ destdir="${build.classes.dir}"
+ includes="sample/vector/Test.java">
+ <classpath refid="classpath"/>
+ </javac>
+ <java fork="true" dir="${run.dir}" classname="sample.vector.Test" />
+ </target>
+
+ <target name = "sample-rmi" depends="sample" >
+ <echo>** Please open sample/rmi/webdemo.html with your browser **</echo>
+ <java fork="true" dir="${run.dir}" classname="sample.rmi.Counter">
+ <classpath refid="classpath"/>
+ <arg value="5001" />
+ </java>
+ </target>
+
+ <target name = "sample-evolve" depends="sample" >
+ <echo>** Please open http://localhost:5003/demo.html with your browser **</echo>
+ <java fork="true" dir="${run.dir}" classname="sample.evolve.DemoLoader">
+ <classpath refid="classpath"/>
+ <arg value="5003" />
+ </java>
+ </target>
+
+</project>
diff --git a/sample/Test.java b/sample/Test.java index 0692943f..07eeda98 100644 --- a/sample/Test.java +++ b/sample/Test.java @@ -23,7 +23,7 @@ public class Test { }
public static void main(String[] args) throws Exception {
- ClassPool pool = ClassPool.getDefault(null);
+ ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("sample.Test");
try {
diff --git a/sample/evolve/DemoLoader.java b/sample/evolve/DemoLoader.java index 5fa950da..ecf45049 100644 --- a/sample/evolve/DemoLoader.java +++ b/sample/evolve/DemoLoader.java @@ -29,12 +29,13 @@ public class DemoLoader { * updatable. Then it runs main() in sample.evolve.DemoServer.
*/
public static void main(String[] args) throws Throwable {
- Evolution translator = new Evolution();
- ClassPool cp = ClassPool.getDefault(translator);
- Loader cl = new Loader();
- cl.setClassPool(cp);
+ Evolution translator = new Evolution();
+ ClassPool cp = ClassPool.getDefault();
+ cp.insertTranslator(translator);
+ Loader cl = new Loader();
+ cl.setClassPool(cp);
- translator.makeUpdatable("sample.evolve.WebPage");
- cl.run("sample.evolve.DemoServer", args);
+ translator.makeUpdatable("sample.evolve.WebPage");
+ cl.run("sample.evolve.DemoServer", args);
}
}
diff --git a/src/main/javassist/AbsClassPool.java b/src/main/javassist/AbsClassPool.java new file mode 100644 index 00000000..5425cab2 --- /dev/null +++ b/src/main/javassist/AbsClassPool.java @@ -0,0 +1,47 @@ +/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2004 Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Abstract root of ClassPool and ClassPoolTail.
+ */
+abstract class AbsClassPool {
+ public abstract String toString();
+
+ public abstract void recordInvalidClassName(String name);
+
+ abstract byte[] readSource(String classname)
+ throws NotFoundException, IOException, CannotCompileException;
+
+ abstract boolean write0(String classname, DataOutputStream out,
+ boolean callback)
+ throws NotFoundException, CannotCompileException, IOException;
+
+ abstract URL find(String classname);
+
+ public abstract ClassPath appendSystemPath();
+ public abstract ClassPath insertClassPath(ClassPath cp);
+ public abstract ClassPath appendClassPath(ClassPath cp);
+ public abstract ClassPath insertClassPath(String pathname)
+ throws NotFoundException;
+ public abstract ClassPath appendClassPath(String pathname)
+ throws NotFoundException;
+ public abstract void removeClassPath(ClassPath cp);
+}
diff --git a/src/main/javassist/ClassPath.java b/src/main/javassist/ClassPath.java index 948b53b5..e249272b 100644 --- a/src/main/javassist/ClassPath.java +++ b/src/main/javassist/ClassPath.java @@ -39,7 +39,7 @@ public interface ClassPath { * <p>This method can return null if the specified class file is not * found. If null is returned, the next search path is examined. * However, if an error happens, this method must throw an exception - * so that the search is terminated. + * so that the search will be terminated. * * <p>This method should not modify the contents of the class file. * Use <code>javassist.Translator</code> for modification. diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index 863750a2..e19915af 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -85,87 +85,63 @@ import java.util.Hashtable; * @see javassist.ClassPath * @see javassist.Translator */ -public class ClassPool { - /* If this field is null, then the object must be an instance of - * ClassPoolTail. - */ - protected ClassPool source; - +public class ClassPool extends AbsClassPool { + protected AbsClassPool source; + protected ClassPool parent; protected Translator translator; - protected Hashtable classes; // should be synchronous - /** - * Provide a hook so that subclasses can do their own - * caching of classes - * - * @see #removeCached(String) - */ - protected CtClass getCached(String classname) - { - return (CtClass)classes.get(classname); - } - - /** - * Provide a hook so that subclasses can do their own - * caching of classes - * - * @see #getCached(String) - */ - protected void removeCached(String classname) - { - classes.remove(classname); - } - /** - * Creates a class pool. - * - * @param src the source of class files. If it is null, - * the class search path is initially null. - * @see javassist.ClassPool#getDefault() + * Table of registered cflow variables. */ - public ClassPool(ClassPool src) { - this(src, null); - } + private Hashtable cflow = null; // should be synchronous. /** * Creates a class pool. * * @param src the source of class files. If it is null, * the class search path is initially null. - * @param trans the translator linked to this class pool. - * It may be null. + * @param p the parent of this class pool. * @see javassist.ClassPool#getDefault() */ - public ClassPool(ClassPool src, Translator trans) - throws RuntimeException - { - classes = new Hashtable(); - CtClass[] pt = CtClass.primitiveTypes; - for (int i = 0; i < pt.length; ++i) - classes.put(pt[i].getName(), pt[i]); + public ClassPool(ClassPool p) { + this(new ClassPoolTail(), p); + } - if (src != null) - source = src; - else - source = new ClassPoolTail(); + ClassPool(AbsClassPool src, ClassPool parent) { + this.classes = new Hashtable(); + this.source = src; + this.parent = parent; + if (parent == null) { + // if this has no parent, it must include primitive types + // even if this.source is not a ClassPoolTail. + CtClass[] pt = CtClass.primitiveTypes; + for (int i = 0; i < pt.length; ++i) + classes.put(pt[i].getName(), pt[i]); + } - translator = trans; - if (trans != null) - try { - trans.start(this); - } - catch (Exception e) { - throw new RuntimeException( - "Translator.start() throws an exception: " - + e.toString()); - } + this.translator = null; + this.cflow = null; } - protected ClassPool() { - source = null; - classes = null; - translator = null; + /** + * Inserts a new translator at the head of the translator chain. + * + * @param trans a new translator associated with this class pool. + * @throws RuntimeException if trans.start() throws an exception. + */ + public void insertTranslator(Translator trans) throws RuntimeException { + ClassPool next = new ClassPool(source, parent); + next.translator = trans; + source = next; + try { + trans.start(next); + } + catch (Exception e) { + throw new RuntimeException( + "Translator.start() throws an exception: " + + e.toString()); + } } /** @@ -188,15 +164,11 @@ public class ClassPool { * * @param t null or the translator linked to the class pool. */ - public static synchronized ClassPool getDefault(Translator t) { + public static synchronized ClassPool getDefault() { if (defaultPool == null) { - ClassPoolTail tail = new ClassPoolTail(); - tail.appendSystemPath(); - defaultPool = new ClassPool(tail, t); + defaultPool = new ClassPool(null); + defaultPool.appendSystemPath(); } - else if (defaultPool.translator != t) - throw new RuntimeException( - "has been created with a different translator"); return defaultPool; } @@ -204,16 +176,26 @@ public class ClassPool { private static ClassPool defaultPool = null; /** - * Returns the default class pool. - * The returned object is always identical. + * Provide a hook so that subclasses can do their own + * caching of classes * - * <p>This returns the result of <code>getDefault(null)</code>. + * @see #removeCached(String) + */ + protected CtClass getCached(String classname) + { + return (CtClass)classes.get(classname); + } + + /** + * Provide a hook so that subclasses can do their own + * caching of classes * - * @see #getDefault(Translator) + * @see #getCached(String) */ - public static ClassPool getDefault() { - return getDefault(null); - } + protected void removeCached(String classname) + { + classes.remove(classname); + } /** * Returns the class search path. @@ -223,8 +205,8 @@ public class ClassPool { } /** - * Records a name that never exists. For example, a package name - * can be recorded by this method. + * Records a name that never exists. + * For example, a package name can be recorded by this method. * This would improve execution performance * since <code>get()</code> does not search the class path at all * if the given name is an invalid name recorded by this method. @@ -239,13 +221,10 @@ public class ClassPool { /** * Returns the <code>Translator</code> object associated with * this <code>ClassPool</code>. + * + * @deprecated */ - public Translator getTranslator() { return translator; } - - /** - * Table of registered cflow variables. - */ - private Hashtable cflow = null; // should be synchronous. + Translator getTranslator() { return translator; } /** * Records the <code>$cflow</code> variable for the field specified @@ -425,7 +404,7 @@ public class ClassPool { /** * Returns a <code>java.lang.Class</code> object that has been loaded - * by <code>writeAsClass()</code>. That object cannot be + * by <code>writeAsClass()</code>. Such an object cannot be * obtained by <code>java.lang.Class.forName()</code> because it has * been loaded by an internal class loader of Javassist. * @@ -526,6 +505,17 @@ public class ClassPool { boolean callback) throws NotFoundException, CannotCompileException, IOException { + if (!write0(classname, out, callback)) + throw new NotFoundException(classname); + } + + boolean write0(String classname, DataOutputStream out, boolean callback) + throws NotFoundException, CannotCompileException, IOException + { + // first, delegate to the parent. + if (parent != null && parent.write0(classname, out, callback)) + return true; + CtClass clazz = (CtClass)getCached(classname); if (callback && translator != null && (clazz == null || !clazz.isFrozen())) { @@ -538,49 +528,76 @@ public class ClassPool { if (clazz != null) clazz.freeze(); - source.write(classname, out); + return source.write0(classname, out, callback); } - else + else { clazz.toBytecode(out); + return true; + } } - /* for CtClassType.getClassFile2() + /* for CtClassType.getClassFile2(). Don't delegate to the parent. */ byte[] readSource(String classname) throws NotFoundException, IOException, CannotCompileException { - return source.write(classname); + return source.readSource(classname); } /* - * Is invoked by CtClassType.setName(). + * Is invoked by CtClassType.setName(). 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); + if (c == clazz) // must check this equation. + removeCached(oldname); // see getAndRename(). String newName = clazz.getName(); - checkNotFrozen(newName, "the class with the new name is frozen."); + checkNotFrozen(newName); classes.put(newName, clazz); } /* * 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, String errmsg) - throws RuntimeException - { - CtClass c = getCached(classname); + void checkNotFrozen(String classname) throws RuntimeException { + CtClass c; + if (parent != null) { + try { + c = parent.get0(classname); + } + catch (NotFoundException e) { // some error happens. + throw new RuntimeException(e.toString()); + } + + if (c != null) + throw new RuntimeException(classname + + " is in a parent ClassPool. Use the parent."); + } + + c = getCached(classname); if (c != null && c.isFrozen()) - throw new RuntimeException(errmsg); + throw new RuntimeException(classname + + ": frozen class (cannot edit)"); } /** * Reads a class file and constructs a <code>CtClass</code> * object with a new name. - * This method is useful if that class file has been already - * loaded and the resulting class is frozen. + * This method is useful if you want to generate a new class as a copy + * of another class (except the class name). For example, + * + * <ul><pre> + * getAndRename("Point", "Pair") + * </pre></ul> + * + * returns a <code>CtClass</code> object representing <code>Pair</code> + * class. The definition of <code>Pair</code> is the same as that of + * <code>Point</code> class except the class name since <code>Pair</code> + * is defined by reading <code>Point.class</code>. * * @param orgName the original (fully-qualified) class name * @param newName the new class name @@ -588,7 +605,16 @@ public class ClassPool { public CtClass getAndRename(String orgName, String newName) throws NotFoundException { - CtClass clazz = get0(orgName); + CtClass clazz = null; + if (parent != null) + clazz = parent.get1(orgName); + + if (clazz == null) + clazz = get1(orgName); + + if (clazz == null) + throw new NotFoundException(orgName); + clazz.setName(newName); // indirectly calls // classNameChanged() in this class return clazz; @@ -610,25 +636,61 @@ public class ClassPool { * * @param classname a fully-qualified class name. */ - public synchronized CtClass get(String classname) + public CtClass get(String classname) throws NotFoundException { + CtClass clazz = get0(classname); + if (clazz == null) + throw new NotFoundException(classname); + else + return clazz; + } + + /** + * @return null if the class could not be found. + */ + private synchronized CtClass get0(String classname) throws NotFoundException { - CtClass clazz = getCached(classname); + CtClass clazz; + if (parent != null) { + clazz = parent.get0(classname); + if (clazz != null) + return clazz; + } + + clazz = getCached(classname); if (clazz == null) { - clazz = get0(classname); - classes.put(classname, clazz); + clazz = get1(classname); + if (clazz != null) + classes.put(classname, clazz); } return clazz; } - protected CtClass get0(String classname) throws NotFoundException { - if (classname.endsWith("[]")) - return new CtArray(classname, this); - else { - checkClassName(classname); - return new CtClassType(classname, this); + private CtClass get1(String classname) throws NotFoundException { + if (classname.endsWith("[]")) { + String base = classname.substring(0, classname.indexOf('[')); + if (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); + } + + /** + * Obtains the URL of the class file specified by classname. + * This method does not delegate to the parent pool. + * + * @param classname a fully-qualified class name. + * @return null if the class file could not be found. + */ + URL find(String classname) { + return source.find(classname); } /** @@ -670,7 +732,7 @@ public class ClassPool { } /** - * Creates a new class from the given class file. + * 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. * @@ -689,8 +751,7 @@ public class ClassPool { CtClass clazz = new CtClassType(classfile, this); clazz.checkModify(); String classname = clazz.getName(); - checkNotFrozen(classname, - "there is a frozen class with the same name."); + checkNotFrozen(classname); classes.put(classname, clazz); return clazz; } @@ -719,8 +780,7 @@ public class ClassPool { public synchronized CtClass makeClass(String classname, CtClass superclass) throws RuntimeException { - checkNotFrozen(classname, - "the class with the given name is frozen."); + checkNotFrozen(classname); CtClass clazz = new CtNewClass(classname, this, false, superclass); classes.put(classname, clazz); return clazz; @@ -750,34 +810,13 @@ public class ClassPool { public synchronized CtClass makeInterface(String name, CtClass superclass) throws RuntimeException { - checkNotFrozen(name, - "the interface with the given name is frozen."); + checkNotFrozen(name); CtClass clazz = new CtNewClass(name, this, true, superclass); classes.put(name, clazz); return clazz; } /** - * Throws an exception if the class with the specified name does not - * exist. - */ - void checkClassName(String classname) - throws NotFoundException - { - source.checkClassName(classname); - } - - /** - * Obtains the URL of the class file specified by classname. - * - * @param classname a fully-qualified class name. - * @return null if the class file could not be found. - */ - public URL find(String classname) { - return source.find(classname); - } - - /** * Appends the system search path to the end of the * search path. The system search path * usually includes the platform library, extension @@ -854,7 +893,7 @@ public class ClassPool { * The detached <code>ClassPath</code> object cannot be added * to the pathagain. */ - public synchronized void removeClassPath(ClassPath cp) { + public void removeClassPath(ClassPath cp) { source.removeClassPath(cp); } diff --git a/src/main/javassist/ClassPoolTail.java b/src/main/javassist/ClassPoolTail.java index 2731ba3d..fb019803 100644 --- a/src/main/javassist/ClassPoolTail.java +++ b/src/main/javassist/ClassPoolTail.java @@ -163,14 +163,12 @@ final class JarClassPath implements ClassPath { } } -final class ClassPoolTail extends ClassPool { +final class ClassPoolTail extends AbsClassPool { protected ClassPathList pathList; - private Class thisClass; private Hashtable packages; // should be synchronized. public ClassPoolTail() { pathList = null; - thisClass = getClass(); packages = new Hashtable(); } @@ -196,33 +194,44 @@ final class ClassPoolTail extends ClassPool { packages.put(name, name); } - public byte[] write(String classname) - throws NotFoundException, IOException + /** + * @return the contents of the class file. + * @throws NotFoundException if the file could not be found. + */ + byte[] readSource(String classname) + throws NotFoundException, IOException, CannotCompileException { - return readClassfile(classname); + byte[] b = readClassfile(classname); + if (b == null) + throw new NotFoundException(classname); + else + return b; } - public void write(String classname, DataOutputStream out) + /** + * @return null if the file could not be found. + * @throws NotFoundException if any error is reported by ClassPath. + */ + boolean write0(String classname, DataOutputStream out, boolean callback) throws NotFoundException, CannotCompileException, IOException { - byte[] b = write(classname); - out.write(b, 0, b.length); - } - - public CtClass get(String classname) throws NotFoundException { - throw new RuntimeException("fatal error"); - } - - public CtClass makeClass(String classname) { - throw new RuntimeException("fatal error"); + byte[] b = readClassfile(classname); + if (b == null) + return false; // not found + else { + out.write(b, 0, b.length); + return true; + } } + /* + -- faster version -- void checkClassName(String classname) throws NotFoundException { if (find(classname) == null) throw new NotFoundException(classname); } - /* slower version. + -- slower version -- void checkClassName(String classname) throws NotFoundException { InputStream fin = openClassfile(classname); @@ -303,11 +312,16 @@ final class ClassPoolTail extends ClassPool { * specified by <code>classname</code>. * * @param classname a fully-qualified class name + * @return null if the file has not been found. + * @throws NotFoundException if any error is reported by ClassPath. */ - byte[] readClassfile(String classname) + private byte[] readClassfile(String classname) throws NotFoundException, IOException { InputStream fin = openClassfile(classname); + if (fin == null) + return null; + byte[] b; try { b = readStream(fin); @@ -323,13 +337,15 @@ final class ClassPoolTail extends ClassPool { * Opens the class file for the class specified by * <code>classname</code>. * - * @param classname a fully-qualified class name + * @param classname a fully-qualified class name + * @return null if the file has not been found. + * @throws NotFoundException if any error is reported by ClassPath. */ - public InputStream openClassfile(String classname) + private InputStream openClassfile(String classname) throws NotFoundException { if (packages.get(classname) != null) - throw new NotFoundException(classname); + return null; // not found ClassPathList list = pathList; InputStream ins = null; @@ -352,7 +368,7 @@ final class ClassPoolTail extends ClassPool { if (error != null) throw error; else - throw new NotFoundException(classname); + return null; // not found } /** diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 596ab0b3..3b57bd67 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -180,8 +180,8 @@ class CtClassType extends CtClass { if (name.equals(oldname)) return; - classPool.checkNotFrozen(name, - "the class with the new name is frozen"); + // check this in advance although classNameChanged() below does. + classPool.checkNotFrozen(name); ClassFile cf = getClassFile2(); super.setName(name); cf.setName(name); @@ -197,8 +197,8 @@ class CtClassType extends CtClass { = (String)classnames.get(Descriptor.toJvmName(oldClassName)); if (newClassName != null) { newClassName = Descriptor.toJavaName(newClassName); - classPool.checkNotFrozen(newClassName, - "the class " + newClassName + " is frozen"); + // check this in advance although classNameChanged() below does. + classPool.checkNotFrozen(newClassName); } super.replaceClassName(classnames); diff --git a/src/main/javassist/Translator.java b/src/main/javassist/Translator.java index f48e8620..57cca039 100644 --- a/src/main/javassist/Translator.java +++ b/src/main/javassist/Translator.java @@ -43,7 +43,7 @@ public interface Translator { * Is invoked by a <code>ClassPool</code> for notifying that * a class is written out to an output stream. * - * <p>If CtClass.frozen() is true, that is, if the class has been + * <p>If <code>CtClass.frozen()</code> is true, that is, if the class has been * already modified and written, then onWrite() is not invoked. * * @param pool the <code>ClassPool</code> that this translator diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java index b156176d..9e759b0d 100644 --- a/src/main/javassist/bytecode/ClassFile.java +++ b/src/main/javassist/bytecode/ClassFile.java @@ -32,636 +32,584 @@ import javassist.bytecode.annotation.AnnotationGroup; * * @see javassist.CtClass#getClassFile() */ -public final class ClassFile -{ - ConstPool constPool; - int thisClass; - int accessFlags; - int superClass; - int[] interfaces; - LinkedList fields; - LinkedList methods; - LinkedList attributes; - AnnotationGroup runtimeInvisible; - AnnotationGroup runtimeVisible; - - String thisclassname; // not JVM-internal name - - /** - * Constructs a class file from a byte stream. - */ - public ClassFile(DataInputStream in) throws IOException - { - read(in); - } - - /** - * Constructs a class file including no members. - * - * @param isInterface true if this is an interface. - * false if this is a class. - * @param classname a fully-qualified class name - * @param superclass a fully-qualified super class name - */ - public ClassFile(boolean isInterface, - String classname, String superclass) - { - constPool = new ConstPool(classname); - thisClass = constPool.getThisClassInfo(); - if (isInterface) - accessFlags = AccessFlag.SUPER | AccessFlag.INTERFACE - | AccessFlag.ABSTRACT; - else - accessFlags = AccessFlag.SUPER; - - initSuperclass(superclass); - interfaces = null; - fields = new LinkedList(); - methods = new LinkedList(); - thisclassname = classname; - - attributes = new LinkedList(); - attributes.add(new SourceFileAttribute(constPool, - getSourcefileName(thisclassname))); - } - - private void initSuperclass(String superclass) - { - if (superclass != null) - superClass = constPool.addClassInfo(superclass); - else - superClass = constPool.addClassInfo("java.lang.Object"); - } - - private static String getSourcefileName(String qname) - { - int index = qname.lastIndexOf('.'); - if (index >= 0) - qname = qname.substring(index + 1); - - return qname + ".java"; - } - - /** - * Returns a constant pool table. - */ - public ConstPool getConstPool() - { - return constPool; - } - - /** - * Returns true if this is an interface. - */ - public boolean isInterface() - { - return (accessFlags & AccessFlag.INTERFACE) != 0; - } - - /** - * Returns true if this is a final class or interface. - */ - public boolean isFinal() - { - return (accessFlags & AccessFlag.FINAL) != 0; - } - - /** - * Returns true if this is an abstract class or an interface. - */ - public boolean isAbstract() - { - return (accessFlags & AccessFlag.ABSTRACT) != 0; - } - - /** - * Returns access flags. - * - * @see javassist.bytecode.AccessFlag - */ - public int getAccessFlags() - { - return accessFlags; - } - - /** - * Changes access flags. - * - * @see javassist.bytecode.AccessFlag - */ - public void setAccessFlags(int acc) - { - accessFlags = acc | AccessFlag.SUPER; - } - - /** - * Returns the class name. - */ - public String getName() - { - return thisclassname; - } - - /** - * Sets the class name. This method substitutes the new name - * for all occurrences of the old class name in the class file. - */ - public void setName(String name) - { - renameClass(thisclassname, name); - } - - /** - * Returns the super class name. - */ - public String getSuperclass() - { - return constPool.getClassInfo(superClass); - } - - /** - * Returns the index of the constant pool entry representing - * the super class. - */ - public int getSuperclassId() - { - return superClass; - } - - /** - * Sets the super class. - * - * <p>This method modifies constructors so that they call - * constructors declared in the new super class. - */ - public void setSuperclass(String superclass) - throws CannotCompileException - { - if (superclass == null) - superclass = "java.lang.Object"; - - try - { - superClass = constPool.addClassInfo(superclass); - LinkedList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - minfo.setSuperclass(superclass); - } - } - catch (BadBytecode e) - { - throw new CannotCompileException(e); - } - } - - /** - * Replaces all occurrences of a class name in the class file. - * - * <p>If class X is substituted for class Y in the class file, - * X and Y must have the same signature. If Y provides a method - * m(), X must provide it even if X inherits m() from the super class. - * If this fact is not guaranteed, the bytecode verifier may cause - * an error. - * - * @param oldname the replaced class name - * @param newname the substituted class name - */ - public final void renameClass(String oldname, String newname) - { - LinkedList list; - int n; - - if (oldname.equals(newname)) - return; - - if (oldname.equals(thisclassname)) - thisclassname = newname; - - oldname = Descriptor.toJvmName(oldname); - newname = Descriptor.toJvmName(newname); - constPool.renameClass(oldname, newname); - - list = methods; - n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - String desc = minfo.getDescriptor(); - minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) - { - FieldInfo finfo = (FieldInfo) list.get(i); - String desc = finfo.getDescriptor(); - finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); - } - } - - /** - * Replaces all occurrences of several class names in the class file. - * - * @param classnames specifies which class name is replaced - * with which new name. Class names must - * be described with the JVM-internal - * representation like - * <code>java/lang/Object</code>. - * - * @see #renameClass(String,String) - */ - public final void renameClass(Map classnames) - { - String jvmNewThisName - = (String) classnames.get(Descriptor.toJvmName(thisclassname)); - if (jvmNewThisName != null) - thisclassname = Descriptor.toJavaName(jvmNewThisName); - - constPool.renameClass(classnames); - - LinkedList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - String desc = minfo.getDescriptor(); - minfo.setDescriptor(Descriptor.rename(desc, classnames)); - } - - list = fields; - n = list.size(); - for (int i = 0; i < n; ++i) - { - FieldInfo finfo = (FieldInfo) list.get(i); - String desc = finfo.getDescriptor(); - finfo.setDescriptor(Descriptor.rename(desc, classnames)); - } - } - - /** - * Returns the names of the interfaces implemented by the class. - */ - public String[] getInterfaces() - { - if (interfaces == null) - return new String[0]; - else - { - int n = interfaces.length; - String[] list = new String[n]; - for (int i = 0; i < n; ++i) - list[i] = constPool.getClassInfo(interfaces[i]); - - return list; - } - } - - /** - * Sets the interfaces. - * - * @param nameList the names of the interfaces. - */ - public void setInterfaces(String[] nameList) - { - if (nameList != null) - { - int n = nameList.length; - interfaces = new int[n]; - for (int i = 0; i < n; ++i) - interfaces[i] = constPool.addClassInfo(nameList[i]); - } - } - - /** - * Appends an interface to the - * interfaces implemented by the class. - */ - public void addInterface(String name) - { - int info = constPool.addClassInfo(name); - if (interfaces == null) - { - interfaces = new int[1]; - interfaces[0] = info; - } - else - { - int n = interfaces.length; - int[] newarray = new int[n + 1]; - System.arraycopy(interfaces, 0, newarray, 0, n); - newarray[n] = info; - interfaces = newarray; - } - } - - /** - * Returns all the fields declared in the class. - * - * @return a list of <code>FieldInfo</code>. - * @see FieldInfo - */ - public List getFields() - { - return fields; - } - - /** - * Appends a field to the class. - */ - public void addField(FieldInfo finfo) throws CannotCompileException - { - testExistingField(finfo.getName(), finfo.getDescriptor()); - fields.add(finfo); - } - - private void addField0(FieldInfo finfo) - { - fields.add(finfo); - } - - private void testExistingField(String name, String descriptor) - throws CannotCompileException - { - ListIterator it = fields.listIterator(0); - while (it.hasNext()) - { - FieldInfo minfo = (FieldInfo) it.next(); - if (minfo.getName().equals(name)) - throw new CannotCompileException("duplicate field: " + name); - } - } - - /** - * Returns all the methods declared in the class. - * - * @return a list of <code>MethodInfo</code>. - * @see MethodInfo - */ - public List getMethods() - { - return methods; - } - - /** - * Returns the method with the specified name. If there are multiple - * methods with that name, this method returns one of them. - * - * @return null if no such a method is found. - */ - public MethodInfo getMethod(String name) - { - LinkedList list = methods; - int n = list.size(); - for (int i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - if (minfo.getName().equals(name)) - return minfo; - } - - return null; - } - - /** - * Returns a static initializer (class initializer), or null if - * it does not exist. - */ - public MethodInfo getStaticInitializer() - { - return getMethod(MethodInfo.nameClinit); - } - - /** - * Appends a method to the class. - */ - public void addMethod(MethodInfo minfo) throws CannotCompileException - { - testExistingMethod(minfo.getName(), minfo.getDescriptor()); - methods.add(minfo); - } - - private void addMethod0(MethodInfo minfo) - { - methods.add(minfo); - } - - private void testExistingMethod(String name, String descriptor) - throws CannotCompileException - { - ListIterator it = methods.listIterator(0); - while (it.hasNext()) - { - MethodInfo minfo = (MethodInfo) it.next(); - if (minfo.getName().equals(name) - && Descriptor.eqSignature(minfo.getDescriptor(), descriptor)) - throw new CannotCompileException("duplicate method: " + name); - } - } - - /** - * Returns all the attributes. - * - * @return a list of <code>AttributeInfo</code> objects. - * @see AttributeInfo - */ - public List getAttributes() - { - return attributes; - } - - /** - * Create an empty (null) attribute "RuntimeInvisibleAnnotations" - * Usually used so that you can start adding annotations to a particular thing - */ - public void createRuntimeInvisibleGroup() - { - if (runtimeInvisible == null) - { - AttributeInfo attr = new AttributeInfo(constPool, "RuntimeInvisibleAnnotations"); - addAttribute(attr); - runtimeInvisible = new AnnotationGroup(attr); - } - } - - /** - * Create an empty (null) attribute "RuntimeVisibleAnnotations" - * Usually used so that you can start adding annotations to a particular thing - */ - public void createRuntimeVisibleGroup() - { - if (runtimeVisible == null) - { - AttributeInfo attr = new AttributeInfo(constPool, "RuntimeVisibleAnnotations"); - addAttribute(attr); - runtimeVisible = new AnnotationGroup(attr); - } - } - - /** - * Return access object for getting info about annotations - * This returns runtime invisible annotations as pertains to the - * CLASS RetentionPolicy - * @return - */ - public AnnotationGroup getRuntimeInvisibleAnnotations() - { - if (runtimeInvisible != null) return runtimeInvisible; - AttributeInfo invisible = getAttribute("RuntimeInvisibleAnnotations"); - if (invisible == null) return null; - runtimeInvisible = new AnnotationGroup(invisible); - return runtimeInvisible; - } - - /** - * Return access object for getting info about annotations - * This returns runtime visible annotations as pertains to the - * RUNTIME RetentionPolicy - * @return - */ - public AnnotationGroup getRuntimeVisibleAnnotations() - { - if (runtimeVisible != null) return runtimeVisible; - AttributeInfo visible = getAttribute("RuntimeVisibleAnnotations"); - if (visible == null) return null; - runtimeVisible = new AnnotationGroup(visible); - return runtimeVisible; - } - - /** - * Returns the attribute with the specified name. - * - * @param name attribute name - */ - public AttributeInfo getAttribute(String name) - { - LinkedList list = attributes; - int n = list.size(); - for (int i = 0; i < n; ++i) - { - AttributeInfo ai = (AttributeInfo) list.get(i); - if (ai.getName().equals(name)) - return ai; - } - - return null; - } - - /** - * Appends an attribute. If there is already an attribute with - * the same name, the new one substitutes for it. - */ - public void addAttribute(AttributeInfo info) - { - AttributeInfo.remove(attributes, info.getName()); - attributes.add(info); - } - - /** - * Returns the source file containing this class. - * - * @return null if this information is not available. - */ - public String getSourceFile() - { - SourceFileAttribute sf - = (SourceFileAttribute) getAttribute(SourceFileAttribute.tag); - if (sf == null) - return null; - else - return sf.getFileName(); - } - - private void read(DataInputStream in) throws IOException - { - int i, n; - int magic = in.readInt(); - if (magic != 0xCAFEBABE) - throw new IOException("non class file"); - - int major = in.readUnsignedShort(); - int minor = in.readUnsignedShort(); - constPool = new ConstPool(in); - accessFlags = in.readUnsignedShort(); - thisClass = in.readUnsignedShort(); - constPool.setThisClassInfo(thisClass); - superClass = in.readUnsignedShort(); - n = in.readUnsignedShort(); - if (n == 0) - interfaces = null; - else - { - interfaces = new int[n]; - for (i = 0; i < n; ++i) - interfaces[i] = in.readUnsignedShort(); - } - - ConstPool cp = constPool; - n = in.readUnsignedShort(); - fields = new LinkedList(); - for (i = 0; i < n; ++i) - addField0(new FieldInfo(cp, in)); - - n = in.readUnsignedShort(); - methods = new LinkedList(); - for (i = 0; i < n; ++i) - addMethod0(new MethodInfo(cp, in)); - - attributes = new LinkedList(); - n = in.readUnsignedShort(); - for (i = 0; i < n; ++i) - addAttribute(AttributeInfo.read(cp, in)); - - thisclassname = constPool.getClassInfo(thisClass); - } - - /** - * Writes a class file represened by this object - * into an output stream. - */ - public void write(DataOutputStream out) throws IOException - { - int i, n; - - out.writeInt(0xCAFEBABE); // magic - out.writeShort(3); // major version - out.writeShort(45); // minor version - constPool.write(out); // constant pool - out.writeShort(accessFlags); - out.writeShort(thisClass); - out.writeShort(superClass); - - if (interfaces == null) - n = 0; - else - n = interfaces.length; - - out.writeShort(n); - for (i = 0; i < n; ++i) - out.writeShort(interfaces[i]); - - LinkedList list = fields; - n = list.size(); - out.writeShort(n); - for (i = 0; i < n; ++i) - { - FieldInfo finfo = (FieldInfo) list.get(i); - finfo.write(out); - } - - list = methods; - n = list.size(); - out.writeShort(n); - for (i = 0; i < n; ++i) - { - MethodInfo minfo = (MethodInfo) list.get(i); - minfo.write(out); - } - - out.writeShort(attributes.size()); - AttributeInfo.writeAll(attributes, out); - } +public final class ClassFile { + ConstPool constPool; + int thisClass; + int accessFlags; + int superClass; + int[] interfaces; + LinkedList fields; + LinkedList methods; + LinkedList attributes; + AnnotationGroup runtimeInvisible; + AnnotationGroup runtimeVisible; + + String thisclassname; // not JVM-internal name + + /** + * Constructs a class file from a byte stream. + */ + public ClassFile(DataInputStream in) throws IOException { + read(in); + } + + /** + * Constructs a class file including no members. + * + * @param isInterface true if this is an interface. + * false if this is a class. + * @param classname a fully-qualified class name + * @param superclass a fully-qualified super class name + */ + public ClassFile(boolean isInterface, + String classname, String superclass) { + constPool = new ConstPool(classname); + thisClass = constPool.getThisClassInfo(); + if (isInterface) + accessFlags = AccessFlag.SUPER | AccessFlag.INTERFACE + | AccessFlag.ABSTRACT; + else + accessFlags = AccessFlag.SUPER; + + initSuperclass(superclass); + interfaces = null; + fields = new LinkedList(); + methods = new LinkedList(); + thisclassname = classname; + + attributes = new LinkedList(); + attributes.add(new SourceFileAttribute(constPool, + getSourcefileName(thisclassname))); + } + + private void initSuperclass(String superclass) { + if (superclass != null) + superClass = constPool.addClassInfo(superclass); + else + superClass = constPool.addClassInfo("java.lang.Object"); + } + + private static String getSourcefileName(String qname) { + int index = qname.lastIndexOf('.'); + if (index >= 0) + qname = qname.substring(index + 1); + + return qname + ".java"; + } + + /** + * Returns a constant pool table. + */ + public ConstPool getConstPool() { + return constPool; + } + + /** + * Returns true if this is an interface. + */ + public boolean isInterface() { + return (accessFlags & AccessFlag.INTERFACE) != 0; + } + + /** + * Returns true if this is a final class or interface. + */ + public boolean isFinal() { + return (accessFlags & AccessFlag.FINAL) != 0; + } + + /** + * Returns true if this is an abstract class or an interface. + */ + public boolean isAbstract() { + return (accessFlags & AccessFlag.ABSTRACT) != 0; + } + + /** + * Returns access flags. + * + * @see javassist.bytecode.AccessFlag + */ + public int getAccessFlags() { + return accessFlags; + } + + /** + * Changes access flags. + * + * @see javassist.bytecode.AccessFlag + */ + public void setAccessFlags(int acc) { + accessFlags = acc | AccessFlag.SUPER; + } + + /** + * Returns the class name. + */ + public String getName() { + return thisclassname; + } + + /** + * Sets the class name. This method substitutes the new name + * for all occurrences of the old class name in the class file. + */ + public void setName(String name) { + renameClass(thisclassname, name); + } + + /** + * Returns the super class name. + */ + public String getSuperclass() { + return constPool.getClassInfo(superClass); + } + + /** + * Returns the index of the constant pool entry representing + * the super class. + */ + public int getSuperclassId() { + return superClass; + } + + /** + * Sets the super class. + * + * <p>This method modifies constructors so that they call + * constructors declared in the new super class. + */ + public void setSuperclass(String superclass) + throws CannotCompileException + { + if (superclass == null) + superclass = "java.lang.Object"; + + try { + superClass = constPool.addClassInfo(superclass); + LinkedList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.setSuperclass(superclass); + } + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + + /** + * Replaces all occurrences of a class name in the class file. + * + * <p>If class X is substituted for class Y in the class file, + * X and Y must have the same signature. If Y provides a method + * m(), X must provide it even if X inherits m() from the super class. + * If this fact is not guaranteed, the bytecode verifier may cause + * an error. + * + * @param oldname the replaced class name + * @param newname the substituted class name + */ + public final void renameClass(String oldname, String newname) { + LinkedList list; + int n; + + if (oldname.equals(newname)) + return; + + if (oldname.equals(thisclassname)) + thisclassname = newname; + + oldname = Descriptor.toJvmName(oldname); + newname = Descriptor.toJvmName(newname); + constPool.renameClass(oldname, newname); + + list = methods; + n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + String desc = minfo.getDescriptor(); + minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + String desc = finfo.getDescriptor(); + finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); + } + } + + /** + * Replaces all occurrences of several class names in the class file. + * + * @param classnames specifies which class name is replaced + * with which new name. Class names must + * be described with the JVM-internal + * representation like + * <code>java/lang/Object</code>. + * + * @see #renameClass(String,String) + */ + public final void renameClass(Map classnames) { + String jvmNewThisName + = (String)classnames.get(Descriptor.toJvmName(thisclassname)); + if (jvmNewThisName != null) + thisclassname = Descriptor.toJavaName(jvmNewThisName); + + constPool.renameClass(classnames); + + LinkedList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + String desc = minfo.getDescriptor(); + minfo.setDescriptor(Descriptor.rename(desc, classnames)); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + String desc = finfo.getDescriptor(); + finfo.setDescriptor(Descriptor.rename(desc, classnames)); + } + } + + /** + * Returns the names of the interfaces implemented by the class. + */ + public String[] getInterfaces() { + if (interfaces == null) + return new String[0]; + else { + int n = interfaces.length; + String[] list = new String[n]; + for (int i = 0; i < n; ++i) + list[i] = constPool.getClassInfo(interfaces[i]); + + return list; + } + } + + /** + * Sets the interfaces. + * + * @param nameList the names of the interfaces. + */ + public void setInterfaces(String[] nameList) { + if (nameList != null) { + int n = nameList.length; + interfaces = new int[n]; + for (int i = 0; i < n; ++i) + interfaces[i] = constPool.addClassInfo(nameList[i]); + } + } + + /** + * Appends an interface to the + * interfaces implemented by the class. + */ + public void addInterface(String name) { + int info = constPool.addClassInfo(name); + if (interfaces == null) { + interfaces = new int[1]; + interfaces[0] = info; + } + else { + int n = interfaces.length; + int[] newarray = new int[n + 1]; + System.arraycopy(interfaces, 0, newarray, 0, n); + newarray[n] = info; + interfaces = newarray; + } + } + + /** + * Returns all the fields declared in the class. + * + * @return a list of <code>FieldInfo</code>. + * @see FieldInfo + */ + public List getFields() { + return fields; + } + + /** + * Appends a field to the class. + */ + public void addField(FieldInfo finfo) throws CannotCompileException { + testExistingField(finfo.getName(), finfo.getDescriptor()); + fields.add(finfo); + } + + private void addField0(FieldInfo finfo) { + fields.add(finfo); + } + + private void testExistingField(String name, String descriptor) + throws CannotCompileException + { + ListIterator it = fields.listIterator(0); + while (it.hasNext()) { + FieldInfo minfo = (FieldInfo)it.next(); + if (minfo.getName().equals(name)) + throw new CannotCompileException("duplicate field: " + name); + } + } + + /** + * Returns all the methods declared in the class. + * + * @return a list of <code>MethodInfo</code>. + * @see MethodInfo + */ + public List getMethods() { + return methods; + } + + /** + * Returns the method with the specified name. If there are multiple + * methods with that name, this method returns one of them. + * + * @return null if no such a method is found. + */ + public MethodInfo getMethod(String name) { + LinkedList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + if (minfo.getName().equals(name)) + return minfo; + } + + return null; + } + + /** + * Returns a static initializer (class initializer), or null if + * it does not exist. + */ + public MethodInfo getStaticInitializer() { + return getMethod(MethodInfo.nameClinit); + } + + /** + * Appends a method to the class. + */ + public void addMethod(MethodInfo minfo) throws CannotCompileException { + testExistingMethod(minfo.getName(), minfo.getDescriptor()); + methods.add(minfo); + } + + private void addMethod0(MethodInfo minfo) { + methods.add(minfo); + } + + private void testExistingMethod(String name, String descriptor) + throws CannotCompileException + { + ListIterator it = methods.listIterator(0); + while (it.hasNext()) { + MethodInfo minfo = (MethodInfo)it.next(); + if (minfo.getName().equals(name) + && Descriptor.eqSignature(minfo.getDescriptor(), descriptor)) + throw new CannotCompileException("duplicate method: " + name); + } + } + + /** + * Returns all the attributes. + * + * @return a list of <code>AttributeInfo</code> objects. + * @see AttributeInfo + */ + public List getAttributes() { + return attributes; + } + + /** + * Create an empty (null) attribute "RuntimeInvisibleAnnotations" + * Usually used so that you can start adding annotations to a particular thing + */ + public void createRuntimeInvisibleGroup() { + if (runtimeInvisible == null) { + AttributeInfo attr = + new AttributeInfo(constPool, "RuntimeInvisibleAnnotations"); + addAttribute(attr); + runtimeInvisible = new AnnotationGroup(attr); + } + } + + /** + * Create an empty (null) attribute "RuntimeVisibleAnnotations" + * Usually used so that you can start adding annotations to a particular thing + */ + public void createRuntimeVisibleGroup() { + if (runtimeVisible == null) { + AttributeInfo attr = + new AttributeInfo(constPool, "RuntimeVisibleAnnotations"); + addAttribute(attr); + runtimeVisible = new AnnotationGroup(attr); + } + } + + /** + * Return access object for getting info about annotations + * This returns runtime invisible annotations as pertains to the + * CLASS RetentionPolicy + * @return + */ + public AnnotationGroup getRuntimeInvisibleAnnotations() { + if (runtimeInvisible != null) + return runtimeInvisible; + AttributeInfo invisible = getAttribute("RuntimeInvisibleAnnotations"); + if (invisible == null) + return null; + runtimeInvisible = new AnnotationGroup(invisible); + return runtimeInvisible; + } + + /** + * Return access object for getting info about annotations + * This returns runtime visible annotations as pertains to the + * RUNTIME RetentionPolicy + * @return + */ + public AnnotationGroup getRuntimeVisibleAnnotations() { + if (runtimeVisible != null) + return runtimeVisible; + AttributeInfo visible = getAttribute("RuntimeVisibleAnnotations"); + if (visible == null) + return null; + runtimeVisible = new AnnotationGroup(visible); + return runtimeVisible; + } + + /** + * Returns the attribute with the specified name. + * + * @param name attribute name + */ + public AttributeInfo getAttribute(String name) { + LinkedList list = attributes; + int n = list.size(); + for (int i = 0; i < n; ++i) { + AttributeInfo ai = (AttributeInfo)list.get(i); + if (ai.getName().equals(name)) + return ai; + } + + return null; + } + + /** + * Appends an attribute. If there is already an attribute with + * the same name, the new one substitutes for it. + */ + public void addAttribute(AttributeInfo info) { + AttributeInfo.remove(attributes, info.getName()); + attributes.add(info); + } + + /** + * Returns the source file containing this class. + * + * @return null if this information is not available. + */ + public String getSourceFile() { + SourceFileAttribute sf + = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag); + if (sf == null) + return null; + else + return sf.getFileName(); + } + + private void read(DataInputStream in) throws IOException { + int i, n; + int magic = in.readInt(); + if (magic != 0xCAFEBABE) + throw new IOException("non class file"); + + int major = in.readUnsignedShort(); + int minor = in.readUnsignedShort(); + constPool = new ConstPool(in); + accessFlags = in.readUnsignedShort(); + thisClass = in.readUnsignedShort(); + constPool.setThisClassInfo(thisClass); + superClass = in.readUnsignedShort(); + n = in.readUnsignedShort(); + if (n == 0) + interfaces = null; + else { + interfaces = new int[n]; + for (i = 0; i < n; ++i) + interfaces[i] = in.readUnsignedShort(); + } + + ConstPool cp = constPool; + n = in.readUnsignedShort(); + fields = new LinkedList(); + for (i = 0; i < n; ++i) + addField0(new FieldInfo(cp, in)); + + n = in.readUnsignedShort(); + methods = new LinkedList(); + for (i = 0; i < n; ++i) + addMethod0(new MethodInfo(cp, in)); + + attributes = new LinkedList(); + n = in.readUnsignedShort(); + for (i = 0; i < n; ++i) + addAttribute(AttributeInfo.read(cp, in)); + + thisclassname = constPool.getClassInfo(thisClass); + } + + /** + * Writes a class file represened by this object + * into an output stream. + */ + public void write(DataOutputStream out) throws IOException { + int i, n; + + out.writeInt(0xCAFEBABE); // magic + out.writeShort(3); // major version + out.writeShort(45); // minor version + constPool.write(out); // constant pool + out.writeShort(accessFlags); + out.writeShort(thisClass); + out.writeShort(superClass); + + if (interfaces == null) + n = 0; + else + n = interfaces.length; + + out.writeShort(n); + for (i = 0; i < n; ++i) + out.writeShort(interfaces[i]); + + LinkedList list = fields; + n = list.size(); + out.writeShort(n); + for (i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + finfo.write(out); + } + + list = methods; + n = list.size(); + out.writeShort(n); + for (i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.write(out); + } + + out.writeShort(attributes.size()); + AttributeInfo.writeAll(attributes, out); + } } diff --git a/src/main/javassist/bytecode/FieldInfo.java b/src/main/javassist/bytecode/FieldInfo.java index 6bb937e2..6ddfcebd 100644 --- a/src/main/javassist/bytecode/FieldInfo.java +++ b/src/main/javassist/bytecode/FieldInfo.java @@ -28,228 +28,211 @@ import java.util.LinkedList; * * @see javassist.CtField#getFieldInfo() */ -public final class FieldInfo -{ - ConstPool constPool; - int accessFlags; - int name; - int descriptor; - LinkedList attribute; // may be null. - AnnotationGroup runtimeInvisible; - AnnotationGroup runtimeVisible; - - private FieldInfo(ConstPool cp) - { - constPool = cp; - accessFlags = 0; - attribute = null; - } - - /** - * Constructs a <code>field_info</code> structure. - * - * @param cp a constant pool table - * @param fieldName field name - * @param desc field descriptor - * - * @see Descriptor - */ - public FieldInfo(ConstPool cp, String fieldName, String desc) - { - this(cp); - name = cp.addUtf8Info(fieldName); - descriptor = cp.addUtf8Info(desc); - } - - FieldInfo(ConstPool cp, DataInputStream in) throws IOException - { - this(cp); - read(in); - } - - /** - * Returns the constant pool table used - * by this <code>field_info</code>. - */ - public ConstPool getConstPool() - { - return constPool; - } - - /** - * Returns the field name. - */ - public String getName() - { - return constPool.getUtf8Info(name); - } - - /** - * Sets the field name. - */ - public void setName(String newName) - { - name = constPool.addUtf8Info(newName); - } - - /** - * Returns the access flags. - * - * @see AccessFlag - */ - public int getAccessFlags() - { - return accessFlags; - } - - /** - * Sets the access flags. - * - * @see AccessFlag - */ - public void setAccessFlags(int acc) - { - accessFlags = acc; - } - - /** - * Returns the field descriptor. - * - * @see Descriptor - */ - public String getDescriptor() - { - return constPool.getUtf8Info(descriptor); - } - - /** - * Sets the field descriptor. - * - * @see Descriptor - */ - public void setDescriptor(String desc) - { - if (!desc.equals(getDescriptor())) - descriptor = constPool.addUtf8Info(desc); - } - - /** - * Returns all the attributes. - * - * @return a list of <code>AttributeInfo</code> objects. - * @see AttributeInfo - */ - public List getAttributes() - { - if (attribute == null) - attribute = new LinkedList(); - - return attribute; - } - - /** - * Returns the attribute with the specified name. - * - * @param name attribute name - */ - 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); - } - - /** - * Create an empty (null) attribute "RuntimeInvisibleAnnotations" - * Usually used so that you can start adding annotations to a particular thing - */ - public void createRuntimeInvisibleGroup() - { - if (runtimeInvisible == null) - { - AttributeInfo attr = new AttributeInfo(constPool, "RuntimeInvisibleAnnotations"); - addAttribute(attr); - runtimeInvisible = new AnnotationGroup(attr); - } - } - - /** - * Create an empty (null) attribute "RuntimeVisibleAnnotations" - * Usually used so that you can start adding annotations to a particular thing - */ - public void createRuntimeVisibleGroup() - { - if (runtimeVisible == null) - { - AttributeInfo attr = new AttributeInfo(constPool, "RuntimeVisibleAnnotations"); - addAttribute(attr); - runtimeVisible = new AnnotationGroup(attr); - } - } - - /** - * Return access object for getting info about annotations - * This returns runtime invisible annotations as pertains to the - * CLASS RetentionPolicy - * @return - */ - public AnnotationGroup getRuntimeInvisibleAnnotations() - { - if (runtimeInvisible != null) return runtimeInvisible; - AttributeInfo invisible = getAttribute("RuntimeInvisibleAnnotations"); - if (invisible == null) return null; - runtimeInvisible = new AnnotationGroup(invisible); - return runtimeInvisible; - } - - /** - * Return access object for getting info about annotations - * This returns runtime visible annotations as pertains to the - * RUNTIME RetentionPolicy - * @return - */ - public AnnotationGroup getRuntimeVisibleAnnotations() - { - if (runtimeVisible != null) return runtimeVisible; - AttributeInfo visible = getAttribute("RuntimeVisibleAnnotations"); - if (visible == null) return null; - runtimeVisible = new AnnotationGroup(visible); - return runtimeVisible; - } - - 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 FieldInfo { + ConstPool constPool; + int accessFlags; + int name; + int descriptor; + LinkedList attribute; // may be null. + AnnotationGroup runtimeInvisible; + AnnotationGroup runtimeVisible; + + private FieldInfo(ConstPool cp) { + constPool = cp; + accessFlags = 0; + attribute = null; + } + + /** + * Constructs a <code>field_info</code> structure. + * + * @param cp a constant pool table + * @param fieldName field name + * @param desc field descriptor + * + * @see Descriptor + */ + public FieldInfo(ConstPool cp, String fieldName, String desc) { + this(cp); + name = cp.addUtf8Info(fieldName); + descriptor = cp.addUtf8Info(desc); + } + + FieldInfo(ConstPool cp, DataInputStream in) throws IOException { + this(cp); + read(in); + } + + /** + * Returns the constant pool table used + * by this <code>field_info</code>. + */ + public ConstPool getConstPool() { + return constPool; + } + + /** + * Returns the field name. + */ + public String getName() { + return constPool.getUtf8Info(name); + } + + /** + * Sets the field name. + */ + public void setName(String newName) { + name = constPool.addUtf8Info(newName); + } + + /** + * Returns the access flags. + * + * @see AccessFlag + */ + public int getAccessFlags() { + return accessFlags; + } + + /** + * Sets the access flags. + * + * @see AccessFlag + */ + public void setAccessFlags(int acc) { + accessFlags = acc; + } + + /** + * Returns the field descriptor. + * + * @see Descriptor + */ + public String getDescriptor() { + return constPool.getUtf8Info(descriptor); + } + + /** + * Sets the field descriptor. + * + * @see Descriptor + */ + public void setDescriptor(String desc) { + if (!desc.equals(getDescriptor())) + descriptor = constPool.addUtf8Info(desc); + } + + /** + * Returns all the attributes. + * + * @return a list of <code>AttributeInfo</code> objects. + * @see AttributeInfo + */ + public List getAttributes() { + if (attribute == null) + attribute = new LinkedList(); + + return attribute; + } + + /** + * Returns the attribute with the specified name. + * + * @param name attribute name + */ + 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); + } + + /** + * Create an empty (null) attribute "RuntimeInvisibleAnnotations" + * Usually used so that you can start adding annotations to a particular thing + */ + public void createRuntimeInvisibleGroup() { + if (runtimeInvisible == null) { + AttributeInfo attr = + new AttributeInfo(constPool, "RuntimeInvisibleAnnotations"); + addAttribute(attr); + runtimeInvisible = new AnnotationGroup(attr); + } + } + + /** + * Create an empty (null) attribute "RuntimeVisibleAnnotations" + * Usually used so that you can start adding annotations to a particular thing + */ + public void createRuntimeVisibleGroup() { + if (runtimeVisible == null) { + AttributeInfo attr = + new AttributeInfo(constPool, "RuntimeVisibleAnnotations"); + addAttribute(attr); + runtimeVisible = new AnnotationGroup(attr); + } + } + + /** + * Return access object for getting info about annotations + * This returns runtime invisible annotations as pertains to the + * CLASS RetentionPolicy + * @return + */ + public AnnotationGroup getRuntimeInvisibleAnnotations() { + if (runtimeInvisible != null) + return runtimeInvisible; + AttributeInfo invisible = getAttribute("RuntimeInvisibleAnnotations"); + if (invisible == null) + return null; + runtimeInvisible = new AnnotationGroup(invisible); + return runtimeInvisible; + } + + /** + * Return access object for getting info about annotations + * This returns runtime visible annotations as pertains to the + * RUNTIME RetentionPolicy + * @return + */ + public AnnotationGroup getRuntimeVisibleAnnotations() { + if (runtimeVisible != null) + return runtimeVisible; + AttributeInfo visible = getAttribute("RuntimeVisibleAnnotations"); + if (visible == null) + return null; + runtimeVisible = new AnnotationGroup(visible); + return runtimeVisible; + } + + 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/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java index ff887ff4..ffe42438 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -30,444 +30,414 @@ 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 - AnnotationGroup runtimeInvisible; - AnnotationGroup runtimeVisible; - - /** - * The name of constructors: <code><init></code>. - */ - public static final String nameInit = "<init>"; - - /** - * The name of class initializer (static initializer): - * <code><clinit></code>. - */ - public static final String nameClinit = "<clinit>"; - - private MethodInfo(ConstPool cp) - { - constPool = cp; - attribute = null; - } - - /** - * Constructs a <code>method_info</code> structure. - * - * @param cp a constant pool table - * @param methodname method name - * @param desc method descriptor - * - * @see Descriptor - */ - public MethodInfo(ConstPool cp, String methodname, String desc) - { - this(cp); - accessFlags = 0; - name = cp.addUtf8Info(methodname); - descriptor = constPool.addUtf8Info(desc); - } - - MethodInfo(ConstPool cp, DataInputStream in) throws IOException - { - this(cp); - read(in); - } - - /** - * Constructs a copy of <code>method_info</code> structure. - * Class names appearing in the source <code>method_info</code> - * are renamed according to <code>classnameMap</code>. - * - * <p>Note: only <code>Code</code> and <code>Exceptions</code> - * attributes are copied from the source. The other attributes - * are ignored. - * - * @param cp a constant pool table - * @param methodname a method name - * @param src a source <code>method_info</code> - * @param classnameMap specifies pairs of replaced and substituted - * name. - * @see Descriptor - */ - public MethodInfo(ConstPool cp, String methodname, MethodInfo src, - Map classnameMap) throws BadBytecode - { - this(cp); - read(src, methodname, classnameMap); - } - - /** - * 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. - * - * @return a list of <code>AttributeInfo</code> objects. - * @see AttributeInfo - */ - public List getAttributes() - { - if (attribute == null) - attribute = new LinkedList(); - - return attribute; - } - - /** - * Returns the attribute with the specified name. - * If it is not found, this method returns null. - * - * @param name attribute name - * @return an <code>AttributeInfo</code> object or null. - */ - public AttributeInfo getAttribute(String name) - { - return AttributeInfo.lookup(attribute, name); - } - - /** - * Appends an attribute. If there is already an attribute with - * the same name, the new one substitutes for it. - */ - public void addAttribute(AttributeInfo info) - { - if (attribute == null) - attribute = new LinkedList(); - - AttributeInfo.remove(attribute, info.getName()); - attribute.add(info); - } - - /** - * Create an empty (null) attribute "RuntimeInvisibleAnnotations" - * Usually used so that you can start adding annotations to a particular thing - */ - public void createRuntimeInvisibleGroup() - { - if (runtimeInvisible == null) - { - AttributeInfo attr = new AttributeInfo(constPool, "RuntimeInvisibleAnnotations"); - addAttribute(attr); - runtimeInvisible = new AnnotationGroup(attr); - } - } - - /** - * Create an empty (null) attribute "RuntimeVisibleAnnotations" - * Usually used so that you can start adding annotations to a particular thing - */ - public void createRuntimeVisibleGroup() - { - if (runtimeVisible == null) - { - AttributeInfo attr = new AttributeInfo(constPool, "RuntimeVisibleAnnotations"); - addAttribute(attr); - runtimeVisible = new AnnotationGroup(attr); - } - } - - /** - * Return access object for getting info about annotations - * This returns runtime invisible annotations as pertains to the - * CLASS RetentionPolicy - * @return - */ - public AnnotationGroup getRuntimeInvisibleAnnotations() - { - if (runtimeInvisible != null) return runtimeInvisible; - AttributeInfo invisible = getAttribute("RuntimeInvisibleAnnotations"); - if (invisible == null) return null; - runtimeInvisible = new AnnotationGroup(invisible); - return runtimeInvisible; - } - - /** - * Return access object for getting info about annotations - * This returns runtime visible annotations as pertains to the - * RUNTIME RetentionPolicy - * @return - */ - public AnnotationGroup getRuntimeVisibleAnnotations() - { - if (runtimeVisible != null) return runtimeVisible; - AttributeInfo visible = getAttribute("RuntimeVisibleAnnotations"); - if (visible == null) return null; - runtimeVisible = new AnnotationGroup(visible); - return runtimeVisible; - } - - /** - * Returns an Exceptions attribute. - * - * @return an Exceptions attribute - * or null if it is not specified. - */ - public ExceptionsAttribute getExceptionsAttribute() - { - AttributeInfo info - = AttributeInfo.lookup(attribute, ExceptionsAttribute.class); - 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.class); - return (CodeAttribute) info; - } - - /** - * Removes an Exception attribute. - */ - public void removeExceptionsAttribute() - { - AttributeInfo.remove(attribute, ExceptionsAttribute.class); - } - - /** - * Adds an Exception attribute. - * - * <p>The added attribute must share the same constant pool table - * as this <code>method_info</code> structure. - */ - public void setExceptionsAttribute(ExceptionsAttribute cattr) - { - removeExceptionsAttribute(); - if (attribute == null) - attribute = new LinkedList(); - - attribute.add(cattr); - } - - /** - * Removes a Code attribute. - */ - public void removeCodeAttribute() - { - AttributeInfo.remove(attribute, CodeAttribute.class); - } - - /** - * Adds a Code attribute. - * - * <p>The added attribute must share the same constant pool table - * as this <code>method_info</code> structure. - */ - public void setCodeAttribute(CodeAttribute cattr) - { - removeCodeAttribute(); - if (attribute == null) - attribute = new LinkedList(); - - attribute.add(cattr); - } - - /** - * Returns the line number of the source line corresponding to the - * specified bytecode contained in this method. - * - * @param pos the position of the bytecode (>= 0). - * an index into the code array. - * @return -1 if this information is not available. - */ - public int getLineNumber(int pos) - { - CodeAttribute ca = getCodeAttribute(); - if (ca == null) - return -1; - - LineNumberAttribute ainfo - = (LineNumberAttribute) ca.getAttribute(LineNumberAttribute.tag); - if (ainfo == null) - return -1; - - return ainfo.toLineNumber(pos); - } - - /** - * Changes a super constructor called by this constructor. - * - * <p>This method modifies a call to <code>super()</code>, - * which should be at the - * head of a constructor body, so that a constructor in a different - * super class is called. This method does not change actural - * parameters. Hence the new super class must have a constructor - * with the same signature as the original one. - * - * <p>This method should be called when the super class - * of the class declaring this method is changed. - * - * <p>This method does not perform anything unless this - * <code>MethodInfo</code> represents a constructor. - * - * @param superclass the new super class - */ - public void setSuperclass(String superclass) throws BadBytecode - { - if (!isConstructor()) - return; - - CodeAttribute ca = getCodeAttribute(); - byte[] code = ca.getCode(); - CodeIterator iterator = ca.iterator(); - int pos = iterator.skipSuperConstructor(); - if (pos >= 0) - { // not this() - ConstPool cp = constPool; - int mref = ByteArray.readU16bit(code, pos + 1); - int nt = cp.getMethodrefNameAndType(mref); - int sc = cp.addClassInfo(superclass); - int mref2 = cp.addMethodrefInfo(sc, nt); - ByteArray.write16bit(mref2, code, pos + 1); - } - } - - private void read(MethodInfo src, String methodname, Map classnames) - throws BadBytecode - { - ConstPool destCp = constPool; - accessFlags = src.accessFlags; - name = destCp.addUtf8Info(methodname); - - ConstPool srcCp = src.constPool; - String desc = srcCp.getUtf8Info(src.descriptor); - String desc2 = Descriptor.rename(desc, classnames); - descriptor = destCp.addUtf8Info(desc2); - - attribute = new LinkedList(); - ExceptionsAttribute eattr = src.getExceptionsAttribute(); - if (eattr != null) - attribute.add(eattr.copy(destCp, classnames)); - - CodeAttribute cattr = src.getCodeAttribute(); - if (cattr != null) - attribute.add(cattr.copy(destCp, classnames)); - } - - private void read(DataInputStream in) throws IOException - { - accessFlags = in.readUnsignedShort(); - name = in.readUnsignedShort(); - descriptor = in.readUnsignedShort(); - int n = in.readUnsignedShort(); - attribute = new LinkedList(); - for (int i = 0; i < n; ++i) - attribute.add(AttributeInfo.read(constPool, in)); - } - - void write(DataOutputStream out) throws IOException - { - out.writeShort(accessFlags); - out.writeShort(name); - out.writeShort(descriptor); - - if (attribute == null) - out.writeShort(0); - else - { - out.writeShort(attribute.size()); - AttributeInfo.writeAll(attribute, out); - } - } +public final class MethodInfo { + ConstPool constPool; + int accessFlags; + int name; + int descriptor; + LinkedList attribute; // may be null + AnnotationGroup runtimeInvisible; + AnnotationGroup runtimeVisible; + + /** + * The name of constructors: <code><init></code>. + */ + public static final String nameInit = "<init>"; + + /** + * The name of class initializer (static initializer): + * <code><clinit></code>. + */ + public static final String nameClinit = "<clinit>"; + + private MethodInfo(ConstPool cp) { + constPool = cp; + attribute = null; + } + + /** + * Constructs a <code>method_info</code> structure. + * + * @param cp a constant pool table + * @param methodname method name + * @param desc method descriptor + * + * @see Descriptor + */ + public MethodInfo(ConstPool cp, String methodname, String desc) { + this(cp); + accessFlags = 0; + name = cp.addUtf8Info(methodname); + descriptor = constPool.addUtf8Info(desc); + } + + MethodInfo(ConstPool cp, DataInputStream in) throws IOException { + this(cp); + read(in); + } + + /** + * Constructs a copy of <code>method_info</code> structure. + * Class names appearing in the source <code>method_info</code> + * are renamed according to <code>classnameMap</code>. + * + * <p>Note: only <code>Code</code> and <code>Exceptions</code> + * attributes are copied from the source. The other attributes + * are ignored. + * + * @param cp a constant pool table + * @param methodname a method name + * @param src a source <code>method_info</code> + * @param classnameMap specifies pairs of replaced and substituted + * name. + * @see Descriptor + */ + public MethodInfo(ConstPool cp, String methodname, MethodInfo src, + Map classnameMap) throws BadBytecode { + this(cp); + read(src, methodname, classnameMap); + } + + /** + * 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. + * + * @return a list of <code>AttributeInfo</code> objects. + * @see AttributeInfo + */ + public List getAttributes() { + if (attribute == null) + attribute = new LinkedList(); + + return attribute; + } + + /** + * Returns the attribute with the specified name. + * If it is not found, this method returns null. + * + * @param name attribute name + * @return an <code>AttributeInfo</code> object or null. + */ + public AttributeInfo getAttribute(String name) { + return AttributeInfo.lookup(attribute, name); + } + + /** + * Appends an attribute. If there is already an attribute with + * the same name, the new one substitutes for it. + */ + public void addAttribute(AttributeInfo info) { + if (attribute == null) + attribute = new LinkedList(); + + AttributeInfo.remove(attribute, info.getName()); + attribute.add(info); + } + + /** + * Create an empty (null) attribute "RuntimeInvisibleAnnotations" + * Usually used so that you can start adding annotations to a particular thing + */ + public void createRuntimeInvisibleGroup() { + if (runtimeInvisible == null) { + AttributeInfo attr = + new AttributeInfo(constPool, "RuntimeInvisibleAnnotations"); + addAttribute(attr); + runtimeInvisible = new AnnotationGroup(attr); + } + } + + /** + * Create an empty (null) attribute "RuntimeVisibleAnnotations" + * Usually used so that you can start adding annotations to a particular thing + */ + public void createRuntimeVisibleGroup() { + if (runtimeVisible == null) { + AttributeInfo attr = + new AttributeInfo(constPool, "RuntimeVisibleAnnotations"); + addAttribute(attr); + runtimeVisible = new AnnotationGroup(attr); + } + } + + /** + * Return access object for getting info about annotations + * This returns runtime invisible annotations as pertains to the + * CLASS RetentionPolicy + * @return + */ + public AnnotationGroup getRuntimeInvisibleAnnotations() { + if (runtimeInvisible != null) + return runtimeInvisible; + AttributeInfo invisible = getAttribute("RuntimeInvisibleAnnotations"); + if (invisible == null) + return null; + runtimeInvisible = new AnnotationGroup(invisible); + return runtimeInvisible; + } + + /** + * Return access object for getting info about annotations + * This returns runtime visible annotations as pertains to the + * RUNTIME RetentionPolicy + * @return + */ + public AnnotationGroup getRuntimeVisibleAnnotations() { + if (runtimeVisible != null) + return runtimeVisible; + AttributeInfo visible = getAttribute("RuntimeVisibleAnnotations"); + if (visible == null) + return null; + runtimeVisible = new AnnotationGroup(visible); + return runtimeVisible; + } + + /** + * Returns an Exceptions attribute. + * + * @return an Exceptions attribute + * or null if it is not specified. + */ + public ExceptionsAttribute getExceptionsAttribute() { + AttributeInfo info + = AttributeInfo.lookup(attribute, ExceptionsAttribute.class); + 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.class); + return (CodeAttribute)info; + } + + /** + * Removes an Exception attribute. + */ + public void removeExceptionsAttribute() { + AttributeInfo.remove(attribute, ExceptionsAttribute.class); + } + + /** + * Adds an Exception attribute. + * + * <p>The added attribute must share the same constant pool table + * as this <code>method_info</code> structure. + */ + public void setExceptionsAttribute(ExceptionsAttribute cattr) { + removeExceptionsAttribute(); + if (attribute == null) + attribute = new LinkedList(); + + attribute.add(cattr); + } + + /** + * Removes a Code attribute. + */ + public void removeCodeAttribute() { + AttributeInfo.remove(attribute, CodeAttribute.class); + } + + /** + * Adds a Code attribute. + * + * <p>The added attribute must share the same constant pool table + * as this <code>method_info</code> structure. + */ + public void setCodeAttribute(CodeAttribute cattr) { + removeCodeAttribute(); + if (attribute == null) + attribute = new LinkedList(); + + attribute.add(cattr); + } + + /** + * Returns the line number of the source line corresponding to the + * specified bytecode contained in this method. + * + * @param pos the position of the bytecode (>= 0). + * an index into the code array. + * @return -1 if this information is not available. + */ + public int getLineNumber(int pos) { + CodeAttribute ca = getCodeAttribute(); + if (ca == null) + return -1; + + LineNumberAttribute ainfo = + (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); + if (ainfo == null) + return -1; + + return ainfo.toLineNumber(pos); + } + + /** + * Changes a super constructor called by this constructor. + * + * <p>This method modifies a call to <code>super()</code>, + * which should be at the + * head of a constructor body, so that a constructor in a different + * super class is called. This method does not change actural + * parameters. Hence the new super class must have a constructor + * with the same signature as the original one. + * + * <p>This method should be called when the super class + * of the class declaring this method is changed. + * + * <p>This method does not perform anything unless this + * <code>MethodInfo</code> represents a constructor. + * + * @param superclass the new super class + */ + public void setSuperclass(String superclass) throws BadBytecode { + if (!isConstructor()) + return; + + CodeAttribute ca = getCodeAttribute(); + byte[] code = ca.getCode(); + CodeIterator iterator = ca.iterator(); + int pos = iterator.skipSuperConstructor(); + if (pos >= 0) { // not this() + ConstPool cp = constPool; + int mref = ByteArray.readU16bit(code, pos + 1); + int nt = cp.getMethodrefNameAndType(mref); + int sc = cp.addClassInfo(superclass); + int mref2 = cp.addMethodrefInfo(sc, nt); + ByteArray.write16bit(mref2, code, pos + 1); + } + } + + private void read(MethodInfo src, String methodname, Map classnames) + throws BadBytecode + { + ConstPool destCp = constPool; + accessFlags = src.accessFlags; + name = destCp.addUtf8Info(methodname); + + ConstPool srcCp = src.constPool; + String desc = srcCp.getUtf8Info(src.descriptor); + String desc2 = Descriptor.rename(desc, classnames); + descriptor = destCp.addUtf8Info(desc2); + + attribute = new LinkedList(); + ExceptionsAttribute eattr = src.getExceptionsAttribute(); + if (eattr != null) + attribute.add(eattr.copy(destCp, classnames)); + + CodeAttribute cattr = src.getCodeAttribute(); + if (cattr != null) + attribute.add(cattr.copy(destCp, classnames)); + } + + private void read(DataInputStream in) throws IOException { + accessFlags = in.readUnsignedShort(); + name = in.readUnsignedShort(); + descriptor = in.readUnsignedShort(); + int n = in.readUnsignedShort(); + attribute = new LinkedList(); + for (int i = 0; i < n; ++i) + attribute.add(AttributeInfo.read(constPool, in)); + } + + void write(DataOutputStream out) throws IOException { + out.writeShort(accessFlags); + out.writeShort(name); + out.writeShort(descriptor); + + if (attribute == null) + out.writeShort(0); + else { + out.writeShort(attribute.size()); + AttributeInfo.writeAll(attribute, out); + } + } } diff --git a/src/main/javassist/reflect/Compiler.java b/src/main/javassist/reflect/Compiler.java index 661908ae..c5650bd1 100644 --- a/src/main/javassist/reflect/Compiler.java +++ b/src/main/javassist/reflect/Compiler.java @@ -90,7 +90,8 @@ public class Compiler { throws Exception { Reflection implementor = new Reflection(); - ClassPool pool = ClassPool.getDefault(implementor); + ClassPool pool = ClassPool.getDefault(); + pool.insertTranslator(implementor); for (int i = 0; i < n; ++i) { CtClass c = pool.get(entries[i].classname); diff --git a/src/main/javassist/reflect/Loader.java b/src/main/javassist/reflect/Loader.java index 62b52932..ee9b3004 100644 --- a/src/main/javassist/reflect/Loader.java +++ b/src/main/javassist/reflect/Loader.java @@ -133,7 +133,9 @@ public class Loader extends javassist.Loader { delegateLoadingOf("javassist.reflect.Loader"); reflection = new Reflection(); - setClassPool(ClassPool.getDefault(reflection)); + ClassPool pool = ClassPool.getDefault(); + pool.insertTranslator(reflection); + setClassPool(pool); } /** diff --git a/src/main/javassist/rmi/AppletServer.java b/src/main/javassist/rmi/AppletServer.java index aab9dbcc..a848c5c4 100644 --- a/src/main/javassist/rmi/AppletServer.java +++ b/src/main/javassist/rmi/AppletServer.java @@ -60,7 +60,7 @@ public class AppletServer extends Webserver { public AppletServer(int port) throws IOException, NotFoundException, CannotCompileException { - this(ClassPool.getDefault(new StubGenerator()), port); + this(ClassPool.getDefault(), new StubGenerator(), port); } /** @@ -72,16 +72,17 @@ public class AppletServer extends Webserver { public AppletServer(int port, ClassPool src) throws IOException, NotFoundException, CannotCompileException { - this(new ClassPool(src, new StubGenerator()), port); + this(new ClassPool(src), new StubGenerator(), port); } - private AppletServer(ClassPool loader, int port) + private AppletServer(ClassPool loader, StubGenerator gen, int port) throws IOException, NotFoundException, CannotCompileException { super(port); exportedNames = new Hashtable(); exportedObjects = new Vector(); - stubGen = (StubGenerator)loader.getTranslator(); + stubGen = gen; + loader.insertTranslator(gen); setClassPool(loader); } |