summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Readme.html7
-rw-r--r--sample/Test.java2
-rw-r--r--sample/evolve/DemoLoader.java3
-rw-r--r--sample/evolve/Evolution.java5
-rw-r--r--sample/vector/VectorAssistant.java4
-rw-r--r--src/main/javassist/ClassPool.java497
-rw-r--r--src/main/javassist/ClassPoolTail.java152
-rw-r--r--src/main/javassist/CtClass.java166
-rw-r--r--src/main/javassist/CtClassType.java32
-rw-r--r--src/main/javassist/CtNewClass.java2
-rw-r--r--src/main/javassist/Loader.java31
-rw-r--r--src/main/javassist/Translator.java38
-rw-r--r--src/main/javassist/reflect/Compiler.java9
-rw-r--r--src/main/javassist/reflect/Loader.java5
-rw-r--r--src/main/javassist/reflect/Reflection.java10
-rw-r--r--src/main/javassist/rmi/AppletServer.java3
-rw-r--r--src/main/javassist/rmi/StubGenerator.java2
-rw-r--r--src/main/javassist/web/Webserver.java31
18 files changed, 461 insertions, 538 deletions
diff --git a/Readme.html b/Readme.html
index c5d0b579..0d2670b0 100644
--- a/Readme.html
+++ b/Readme.html
@@ -255,10 +255,15 @@ see javassist.Dump.
<h2>Changes</h2>
-<p>- version 2.7
+<p>- version 3.0
<ul>
<li>javassist.bytecode.annotation has been added.
+ <li>The ClassPool framework has been redesigned.
+ <ul>
+ <li>writeFile(), write(), ... in ClassPool have been moved to CtClass.
+ <li>The design of javassist.Translator has been changed.
+ </ul>
<li>Now local variables were made available in the source text passed to
CtBehavior.insertBefore(), MethodCall.replace(), etc.
diff --git a/sample/Test.java b/sample/Test.java
index 810b6b61..1e2b49c2 100644
--- a/sample/Test.java
+++ b/sample/Test.java
@@ -37,7 +37,7 @@ public class Test {
CtMethod fMethod = cc.getDeclaredMethod("f");
CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);
cc.addMethod(gMethod);
- pool.writeFile("sample.Test"); // update the class file
+ cc.writeFile(); // update the class file
System.out.println("g() was added.");
}
}
diff --git a/sample/evolve/DemoLoader.java b/sample/evolve/DemoLoader.java
index 1d8c33f9..f1474a1f 100644
--- a/sample/evolve/DemoLoader.java
+++ b/sample/evolve/DemoLoader.java
@@ -31,9 +31,8 @@ public class DemoLoader {
public static void main(String[] args) throws Throwable {
Evolution translator = new Evolution();
ClassPool cp = ClassPool.getDefault();
- cp.addTranslator(translator);
Loader cl = new Loader();
- cl.setClassPool(cp);
+ cl.addTranslator(cp, translator);
translator.makeUpdatable("sample.evolve.WebPage");
cl.run("sample.evolve.DemoServer", args);
diff --git a/sample/evolve/Evolution.java b/sample/evolve/Evolution.java
index 810f1986..ad6d1281 100644
--- a/sample/evolve/Evolution.java
+++ b/sample/evolve/Evolution.java
@@ -43,16 +43,15 @@ public class Evolution implements Translator {
trapMethod = _pool.getMethod("sample.evolve.Sample", "make");
}
- public void onWrite(ClassPool _pool, String classname)
+ public void onWrite(ClassPool _pool, CtClass clazz)
throws NotFoundException, CannotCompileException
{
- onWriteUpdatable(classname);
+ onWriteUpdatable(clazz.getName());
/*
* Replaces all the occurrences of the new operator with a call
* to _makeInstance().
*/
- CtClass clazz = _pool.get(classname);
CtClass absClass = updatableClass;
CodeConverter converter = new CodeConverter();
converter.replaceNew(absClass, absClass, handlerMethod);
diff --git a/sample/vector/VectorAssistant.java b/sample/vector/VectorAssistant.java
index 44fdd41c..27d5f702 100644
--- a/sample/vector/VectorAssistant.java
+++ b/sample/vector/VectorAssistant.java
@@ -96,7 +96,7 @@ public class VectorAssistant implements Assistant {
vec.addMethod(CtNewMethod.copy(addmethod, "add", vec, map));
vec.addMethod(CtNewMethod.copy(atmethod, "at", vec, map));
- pool.writeFile(vec.getName());
+ vec.writeFile();
return vec;
}
@@ -125,7 +125,7 @@ public class VectorAssistant implements Assistant {
m = CtNewMethod.wrapped(type, "at", args2,
null, atmethod, null, vec);
vec.addMethod(m);
- pool.writeFile(vec.getName());
+ vec.writeFile();
return vec;
}
diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java
index 137ad3a9..cd239522 100644
--- a/src/main/javassist/ClassPool.java
+++ b/src/main/javassist/ClassPool.java
@@ -83,10 +83,23 @@ import java.util.Hashtable;
* @see javassist.ClassPath
* @see javassist.Translator
*/
-public class ClassPool extends AbsClassPool {
- protected AbsClassPool source;
+public class ClassPool {
+
+ /**
+ * Determines the search order.
+ *
+ * <p>If this field is true, <code>get()</code> first searches the
+ * class path associated to this <code>ClassPool</code> and then
+ * the class path associated with the parent <code>ClassPool</code>.
+ * Otherwise, the class path associated with the parent is searched
+ * first.
+ *
+ * <p>The default value is false.
+ */
+ public boolean childFirstLookup = false;
+
+ protected ClassPoolTail source;
protected ClassPool parent;
- protected Translator translator;
protected Hashtable classes; // should be synchronous
/**
@@ -97,62 +110,24 @@ public class ClassPool extends AbsClassPool {
/**
* Creates a class pool.
*
- * @param src the source of class files. If it is null,
- * the class search path is initially null.
- * @param p the parent of this class pool.
+ * @param p the parent of this class pool. If this is a root
+ * class pool, this parameter must be <code>null</code>.
* @see javassist.ClassPool#getDefault()
*/
- public ClassPool(ClassPool p) {
- this(new ClassPoolTail(), p);
- }
-
- ClassPool(AbsClassPool src, ClassPool parent) {
+ public ClassPool(ClassPool parent) {
this.classes = new Hashtable();
- this.source = src;
+ this.source = new ClassPoolTail();
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]);
}
- this.translator = null;
this.cflow = null;
}
/**
- * Adds a new translator at the end of the translator chain.
- *
- * @param trans a new translator associated with this class pool.
- * @throws RuntimeException if trans.start() throws an exception.
- */
- public void addTranslator(Translator trans) throws RuntimeException {
- ClassPool cp;
- if (translator == null)
- cp = this;
- else {
- ClassPool s = this;
- while (s.source instanceof ClassPool)
- s = (ClassPool)s.source;
-
- cp = new ClassPool(s.source, parent);
- s.source = cp;
- }
-
- cp.translator = trans;
- try {
- trans.start(cp);
- }
- catch (Exception e) {
- throw new RuntimeException(
- "Translator.start() throws an exception: "
- + e.toString());
- }
- }
-
- /**
* Returns the default class pool.
* The returned object is always identical since this method is
* a singleton factory.
@@ -210,12 +185,12 @@ public class ClassPool extends AbsClassPool {
classes.remove(classname);
}
- /**
- * Returns the class search path.
- */
- public String toString() {
- return source.toString();
- }
+ /**
+ * Returns the class search path.
+ */
+ public String toString() {
+ return source.toString();
+ }
/**
* Records a name that never exists.
@@ -232,14 +207,6 @@ public class ClassPool extends AbsClassPool {
}
/**
- * Returns the <code>Translator</code> object associated with
- * this <code>ClassPool</code>.
- *
- * @deprecated
- */
- Translator getTranslator() { return translator; }
-
- /**
* Records the <code>$cflow</code> variable for the field specified
* by <code>cname</code> and <code>fname</code>.
*
@@ -267,133 +234,12 @@ public class ClassPool extends AbsClassPool {
}
/**
- * Writes a class file specified with <code>classname</code>
- * in the current directory.
- * It never calls <code>onWrite()</code> on a translator.
- * It is provided for debugging.
- *
- * @param classname the name of the class written on a local disk.
- */
- public void debugWriteFile(String classname)
- throws NotFoundException, CannotCompileException, IOException
- {
- debugWriteFile(classname, ".");
- }
-
- /**
- * Writes a class file specified with <code>classname</code>.
- * It never calls <code>onWrite()</code> on a translator.
- * It is provided for debugging.
- *
- * @param classname the name of the class written on a local disk.
- * @param directoryName it must end without a directory separator.
- */
- public void debugWriteFile(String classname, String directoryName)
- throws NotFoundException, CannotCompileException, IOException
- {
- writeFile(classname, directoryName, false);
- }
-
- /* void writeFile(CtClass) should not be defined since writeFile()
- * may be called on the class pool that does not contain the given
- * CtClass object.
- */
-
- /**
- * Writes a class file specified with <code>classname</code>
- * in the current directory.
- * It calls <code>onWrite()</code> on a translator.
- *
- * @param classname the name of the class written on a local disk.
- */
- public void writeFile(String classname)
- throws NotFoundException, CannotCompileException, IOException
- {
- writeFile(classname, ".");
- }
-
- /**
- * Writes a class file specified with <code>classname</code>
- * on a local disk.
- * It calls <code>onWrite()</code> on a translator.
- *
- * @param classname the name of the class written on a local disk.
- * @param directoryName it must end without a directory separator.
- */
- public void writeFile(String classname, String directoryName)
- throws NotFoundException, CannotCompileException, IOException
- {
- writeFile(classname, directoryName, true);
- }
-
- private void writeFile(String classname, String directoryName,
- boolean callback)
- throws NotFoundException, CannotCompileException, IOException
- {
- 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)));
- write(classname, out, callback);
- 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();
- }
- }
-
- /**
- * A simple class loader used by <code>writeAsClass()</code>
- * in <code>ClassPool</code>.
+ * A simple class loader used by <code>toClass()</code>
+ * in <code>CtClass</code>.
* This class loader is provided for convenience. If you need more
* complex functionality, you should write your own class loader.
*
- * @see ClassPool#writeAsClass(String)
+ * @see CtClass#forName(String)
* @see CtClass#toClass()
*/
public static class SimpleLoader extends ClassLoader {
@@ -413,64 +259,29 @@ public class ClassPool extends AbsClassPool {
}
};
- private static SimpleLoader classLoader = new SimpleLoader();
+ private static SimpleLoader classLoader = null;
/**
* Returns a <code>java.lang.Class</code> object that has been loaded
- * by <code>writeAsClass()</code>. Such an object cannot be
+ * by <code>loadClass()</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.
- *
- * @see #writeAsClass(String)
- * @see javassist.CtClass#toClass()
*/
- public static Class forName(String name) throws ClassNotFoundException {
+ static Class forName(String name) throws ClassNotFoundException {
+ if (classLoader == null)
+ classLoader = new SimpleLoader();
+
return classLoader.loadClass(name);
}
- /**
- * Returns a <code>java.lang.Class</code> object.
- * It calls <code>write()</code> to obtain a class file and then
- * loads the obtained class file into the JVM. The returned
- * <code>Class</code> object represents the loaded class.
- *
- * <p>This method is provided for convenience. If you need more
- * complex functionality, you should write your own class loader.
- *
- * <p>To load a class file, this method uses an internal class loader,
- * which is an instance of <code>ClassPool.SimpleLoader</code>.
- * Thus, that class file is not loaded by the system class loader,
- * which should have loaded this <code>ClassPool</code> class.
- * The internal class loader
- * loads only the classes explicitly specified by this method
- * <code>writeAsClass()</code>. The other classes are loaded
- * by the parent class loader (the sytem class loader) by delegation.
- *
- * <p>For example,
- *
- * <ul><pre>class Line { Point p1, p2; }</pre></ul>
- *
- * <p>If the class <code>Line</code> is loaded by the internal class
- * loader and the class <code>Point</code> has not been loaded yet,
- * then the class <code>Point</code> that the class <code>Line</code>
- * refers to is loaded by the parent class loader. There is no
- * chance of modifying the definition of <code>Point</code> with
- * Javassist.
- *
- * <p>The internal class loader is shared among all the instances
- * of <code>ClassPool</code>.
- *
- * @param classname a fully-qualified class name.
- *
- * @see #forName(String)
- * @see javassist.CtClass#toClass()
- * @see javassist.Loader
- */
- public Class writeAsClass(String classname)
+ static Class loadClass(String classname, byte[] classfile)
throws NotFoundException, IOException, CannotCompileException
{
+ if (classLoader == null)
+ classLoader = new SimpleLoader();
+
try {
- return classLoader.loadClass(classname, write(classname));
+ return classLoader.loadClass(classname, classfile);
}
catch (ClassFormatError e) {
throw new CannotCompileException(e, classname);
@@ -478,122 +289,6 @@ public class ClassPool extends AbsClassPool {
}
/**
- * Returns a byte array representing the class file.
- * It calls <code>onWrite()</code> on a translator.
- *
- * @param classname a fully-qualified class name.
- */
- public byte[] write(String classname)
- throws NotFoundException, IOException, CannotCompileException
- {
- ByteArrayOutputStream barray = new ByteArrayOutputStream();
- DataOutputStream out = new DataOutputStream(barray);
- try {
- write(classname, out, true);
- }
- finally {
- out.close();
- }
-
- return barray.toByteArray();
- }
-
- /**
- * Writes a class file specified by <code>classname</code>
- * to a given output stream.
- * It calls <code>onWrite()</code> on a translator.
- *
- * <p>This method does not close the output stream in the end.
- *
- * @param classname a fully-qualified class name.
- * @param out an output stream
- */
- public void write(String classname, DataOutputStream out)
- throws NotFoundException, CannotCompileException, IOException
- {
- write(classname, out, true);
- }
-
- private void write(String classname, DataOutputStream out,
- 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
- {
- CtClass clazz = (CtClass)getCached(classname);
- if (callback && translator != null
- && (clazz == null || !clazz.isFrozen())) {
- translator.onWrite(this, classname);
- // The CtClass object might be overwritten.
- clazz = (CtClass)getCached(classname);
- }
-
- if (clazz == null || !clazz.isModified()) {
- if (clazz != null)
- clazz.freeze();
-
- return source.write0(classname, out, callback);
- }
- else {
- clazz.toBytecode(out);
- return true;
- }
- }
-
- /* for CtClassType.getClassFile2(). Don't delegate to the parent.
- */
- byte[] readSource(String classname)
- throws NotFoundException, IOException, CannotCompileException
- {
- return source.readSource(classname);
- }
-
- /*
- * 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); // see getAndRename().
-
- String newName = clazz.getName();
- 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) 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(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 you want to generate a new class as a copy
@@ -614,21 +309,30 @@ public class ClassPool extends AbsClassPool {
public CtClass getAndRename(String orgName, String newName)
throws NotFoundException
{
- CtClass clazz = null;
- if (parent != null)
- clazz = parent.get1(orgName);
-
- if (clazz == null)
- clazz = get1(orgName);
-
- if (clazz == null)
- throw new NotFoundException(orgName);
+ 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);
+ classes.put(newName, clazz);
+ }
+
/**
* Reads a class file from the source and returns a reference
* to the <code>CtClass</code>
@@ -646,7 +350,7 @@ public class ClassPool extends AbsClassPool {
* @param classname a fully-qualified class name.
*/
public CtClass get(String classname) throws NotFoundException {
- CtClass clazz = get0(classname);
+ CtClass clazz = get0(classname, true);
if (clazz == null)
throw new NotFoundException(classname);
else
@@ -654,33 +358,51 @@ public class ClassPool extends AbsClassPool {
}
/**
+ * @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.
*/
- private synchronized CtClass get0(String classname)
+ protected synchronized CtClass get0(String classname, boolean useCache)
throws NotFoundException
{
- CtClass clazz;
- clazz = getCached(classname);
- if (clazz != null) return clazz;
- if (parent != null) {
- clazz = parent.get0(classname);
+ CtClass clazz = null;
+ if (useCache) {
+ clazz = getCached(classname);
if (clazz != null)
return clazz;
}
- if (clazz == null) {
- clazz = get1(classname);
+ if (!childFirstLookup && parent != null) {
+ clazz = parent.get0(classname, useCache);
if (clazz != null)
+ return clazz;
+ }
+
+ clazz = createCtClass(classname, useCache);
+ if (clazz != null) {
+ if (useCache)
classes.put(classname, clazz);
+
+ return clazz;
}
+ if (childFirstLookup && parent != null)
+ clazz = parent.get0(classname, useCache);
+
return clazz;
}
- protected CtClass get1(String classname) throws NotFoundException {
+ /**
+ * 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.
+ */
+ private CtClass createCtClass(String classname, boolean useCache) {
if (classname.endsWith("[]")) {
String base = classname.substring(0, classname.indexOf('['));
- if (getCached(base) == null && find(base) == null)
+ if ((!useCache || getCached(base) == null) && find(base) == null)
return null;
else
return new CtArray(classname, this);
@@ -693,16 +415,57 @@ public class ClassPool extends AbsClassPool {
}
/**
- * Obtains the URL of the class file specified by classname.
- * This method does not delegate to the parent pool.
+ * 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 CtClassType#getURL()
*/
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.
+ */
+ byte[] readSource(String classname)
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ return source.readSource(classname);
+ }
+
+ void writeClassfile(String classname, OutputStream out)
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ source.writeClassfile(classname, out);
+ }
+
/**
* Reads class files from the source and returns an array of
* <code>CtClass</code>
diff --git a/src/main/javassist/ClassPoolTail.java b/src/main/javassist/ClassPoolTail.java
index 4abe8e7c..5cfc2933 100644
--- a/src/main/javassist/ClassPoolTail.java
+++ b/src/main/javassist/ClassPoolTail.java
@@ -128,7 +128,7 @@ final class JarClassPath implements ClassPath {
}
}
-final class ClassPoolTail extends AbsClassPool {
+final class ClassPoolTail {
protected ClassPathList pathList;
private Hashtable packages; // should be synchronized.
@@ -151,62 +151,6 @@ final class ClassPoolTail extends AbsClassPool {
return buf.toString();
}
- /**
- * You can record "System" so that java.lang.System can be quickly
- * found although "System" is not a package name.
- */
- public void recordInvalidClassName(String name) {
- packages.put(name, name);
- }
-
- /**
- * @return the contents of the class file.
- * @throws NotFoundException if the file could not be found.
- */
- byte[] readSource(String classname)
- throws NotFoundException, IOException, CannotCompileException
- {
- byte[] b = readClassfile(classname);
- if (b == null)
- throw new NotFoundException(classname);
- else
- return b;
- }
-
- /**
- * @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 = 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 --
-
- void checkClassName(String classname) throws NotFoundException {
- InputStream fin = openClassfile(classname);
- try {
- fin.close();
- }
- catch (IOException e) {}
- }
- */
-
public synchronized ClassPath insertClassPath(ClassPath cp) {
pathList = new ClassPathList(cp, pathList);
return cp;
@@ -273,6 +217,65 @@ final class ClassPoolTail extends AbsClassPool {
}
/**
+ * You can record "System" so that java.lang.System can be quickly
+ * found although "System" is not a package name.
+ */
+ public void recordInvalidClassName(String name) {
+ packages.put(name, name);
+ }
+
+ /**
+ * @return the contents of the class file.
+ * @throws NotFoundException if the file could not be found.
+ */
+ byte[] readSource(String classname)
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ byte[] b = readClassfile(classname);
+ if (b == null)
+ throw new NotFoundException(classname);
+ else
+ return b;
+ }
+
+ /**
+ * This method does not close the output stream.
+ */
+ void writeClassfile(String classname, OutputStream out)
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ InputStream fin = openClassfile(classname);
+ if (fin == null)
+ throw new NotFoundException(classname);
+
+ try {
+ copyStream(fin, out);
+ }
+ finally {
+ fin.close();
+ }
+ }
+
+ /*
+ -- faster version --
+ void checkClassName(String classname) throws NotFoundException {
+ if (find(classname) == null)
+ throw new NotFoundException(classname);
+ }
+
+ -- slower version --
+
+ void checkClassName(String classname) throws NotFoundException {
+ InputStream fin = openClassfile(classname);
+ try {
+ fin.close();
+ }
+ catch (IOException e) {}
+ }
+ */
+
+
+ /**
* Obtains the contents of the class file for the class
* specified by <code>classname</code>.
*
@@ -337,7 +340,9 @@ final class ClassPoolTail extends AbsClassPool {
}
/**
- * Obtains the URL of the class file specified by classname.
+ * 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.
@@ -360,7 +365,7 @@ final class ClassPoolTail extends AbsClassPool {
}
/**
- * Reads an input stream until it reaches the end.
+ * Reads from an input stream until it reaches the end.
*
* @return the contents of that input stream
*/
@@ -393,4 +398,33 @@ final class ClassPoolTail extends AbsClassPool {
throw new IOException("too much data");
}
+
+ /**
+ * Reads from an input stream and write to an output stream
+ * until it reaches the end. This method does not close the
+ * streams.
+ */
+ public static void copyStream(InputStream fin, OutputStream fout)
+ throws IOException
+ {
+ int bufsize = 4096;
+ for (int i = 0; i < 8; ++i) {
+ byte[] buf = new byte[bufsize];
+ int size = 0;
+ int len = 0;
+ do {
+ len = fin.read(buf, size, bufsize - size);
+ if (len >= 0)
+ size += len;
+ else {
+ fout.write(buf, 0, size);
+ return;
+ }
+ } while (size < bufsize);
+ fout.write(buf);
+ bufsize *= 2;
+ }
+
+ throw new IOException("too much data");
+ }
}
diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java
index 4c6f7bd7..1b9f14d8 100644
--- a/src/main/javassist/CtClass.java
+++ b/src/main/javassist/CtClass.java
@@ -15,8 +15,7 @@
package javassist;
-import java.io.DataOutputStream;
-import java.io.IOException;
+import java.io.*;
import javassist.bytecode.*;
import java.util.Collection;
import javassist.expr.ExprEditor;
@@ -36,7 +35,7 @@ public abstract class CtClass {
/**
* The version number of this release.
*/
- public static final String version = "2.7 alpha 8";
+ public static final String version = "3.0 alpha 1";
/**
* Prints the version number and the copyright notice.
@@ -815,22 +814,55 @@ public abstract class CtClass {
* Once this method is called, further modifications are not
* possible any more.
*
- * <p>This method is equivalent to:
- * <ul><pre>this.getClassPool().writeAsClass(this.getName())</pre></ul>
- *
- * <p>See the description of <code>ClassPool.writeAsClass()</code>
- * before you use this method.
- * This method is provided for convenience. If you need more
+ * <p>This method is provided for convenience. If you need more
* complex functionality, you should write your own class loader.
*
- * @see javassist.ClassPool#writeAsClass(String)
- * @see javassist.ClassPool#forName(String)
- * @see javassist.Loader
+ * <p>To load a class file, this method uses an internal class loader,
+ * which is an instance of <code>ClassPool.SimpleLoader</code>.
+ * Thus, that class file is not loaded by the system class loader,
+ * which should have loaded this <code>CtClass</code> class.
+ * The internal class loader
+ * loads only the classes explicitly specified by this method
+ * <code>toClass()</code>. The other classes are loaded
+ * by the parent class loader (usually the sytem class loader)
+ * by delegation.
+ *
+ * <p>For example,
+ *
+ * <ul><pre>class Line { Point p1, p2; }</pre></ul>
+ *
+ * <p>If the class <code>Line</code> is loaded by the internal class
+ * loader and the class <code>Point</code> has not been loaded yet,
+ * then the class <code>Point</code> that the class <code>Line</code>
+ * refers to is loaded by the parent class loader. There is no
+ * chance of modifying the definition of <code>Point</code> with
+ * Javassist.
+ *
+ * <p>The internal class loader is shared among all the instances
+ * of <code>ClassPool</code>.
+ *
+ * @return the <code>Class</code> object representing the loaded class.
+ * @see CtClass#forName(String)
+ * @see ClassPool.SimpleClassLoader
+ * @see Loader
*/
public Class toClass()
throws NotFoundException, IOException, CannotCompileException
{
- return getClassPool2().writeAsClass(getName());
+ return ClassPool.loadClass(getName(), toBytecode());
+ }
+
+ /**
+ * Returns a <code>java.lang.Class</code> object that has been loaded
+ * by <code>toClass()</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.
+ *
+ * @see CtClass#toClass()
+ * @see ClassPool.SimpleClassLoader
+ */
+ public static Class forName(String name) throws ClassNotFoundException {
+ return ClassPool.forName(name);
}
/**
@@ -838,15 +870,21 @@ public abstract class CtClass {
* Once this method is called, further modifications are not
* possible any more.
*
- * <p>This method is equivalent to:
- * <ul><pre>this.getClassPool().write(this.getName())</pre></ul>
- *
- * @see javassist.ClassPool#write(String)
+ * @return the contents of the class file.
*/
public byte[] toBytecode()
throws NotFoundException, IOException, CannotCompileException
{
- return getClassPool2().write(getName());
+ ByteArrayOutputStream barray = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(barray);
+ try {
+ toBytecode(out);
+ }
+ finally {
+ out.close();
+ }
+
+ return barray.toByteArray();
}
/**
@@ -854,25 +892,84 @@ public abstract class CtClass {
* object in the current directory.
* Once this method is called, further modifications are not
* possible any more.
- *
- * <p>This method is equivalent to:
- * <ul><pre>this.getClassPool().writeFile(this.getName())</pre></ul>
- *
- * @see javassist.ClassPool#writeFile(String)
*/
public void writeFile()
throws NotFoundException, IOException, CannotCompileException
{
- getClassPool2().writeFile(getName());
+ writeFile(".");
}
- private ClassPool getClassPool2() throws CannotCompileException {
- ClassPool cp = getClassPool();
- if (cp == null)
- throw new CannotCompileException(
- getName() + ": no ClassPool found. not a class?");
- else
- return cp;
+ /**
+ * Writes a class file represented by this <code>CtClass</code>
+ * object on a local disk.
+ * Once this method is called, further modifications are not
+ * possible any more.
+ *
+ * @param directoryName it must end without a directory separator.
+ */
+ public void writeFile(String directoryName)
+ throws NotFoundException, CannotCompileException, IOException
+ {
+ String classname = getName();
+ String filename = directoryName + File.separatorChar
+ + classname.replace('.', File.separatorChar) + ".class";
+ int pos = filename.lastIndexOf(File.separatorChar);
+ if (pos > 0) {
+ String dir = filename.substring(0, pos);
+ if (!dir.equals("."))
+ new File(dir).mkdirs();
+ }
+
+ DataOutputStream out
+ = new DataOutputStream(new BufferedOutputStream(
+ new DelayedFileOutputStream(filename)));
+ try {
+ toBytecode(out);
+ }
+ finally {
+ out.close();
+ }
+ }
+
+ static class DelayedFileOutputStream extends OutputStream {
+ private FileOutputStream file;
+ private String filename;
+
+ DelayedFileOutputStream(String name) {
+ file = null;
+ filename = name;
+ }
+
+ private void init() throws IOException {
+ if (file == null)
+ file = new FileOutputStream(filename);
+ }
+
+ public void write(int b) throws IOException {
+ init();
+ file.write(b);
+ }
+
+ public void write(byte[] b) throws IOException {
+ init();
+ file.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ init();
+ file.write(b, off, len);
+
+ }
+
+ public void flush() throws IOException {
+ init();
+ file.flush();
+ }
+
+ public void close() throws IOException {
+ init();
+ file.close();
+ }
}
/**
@@ -880,16 +977,11 @@ public abstract class CtClass {
* Once this method is called, further modifications are not
* possible any more.
*
- * <p>If this method is used to obtain a byte array representing
- * the class file, <code>Translator.onWrite()</code> is never
- * called on this class. <code>ClassPool.write()</code> should
- * be used.
- *
* <p>This method dose not close the output stream in the end.
*
* @param out the output stream that a class file is written to.
*/
- void toBytecode(DataOutputStream out)
+ 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 3b57bd67..c74acbf5 100644
--- a/src/main/javassist/CtClassType.java
+++ b/src/main/javassist/CtClassType.java
@@ -34,10 +34,10 @@ import java.util.List;
* Class types.
*/
class CtClassType extends CtClass {
- protected ClassPool classPool;
- protected boolean wasChanged;
- protected boolean wasFrozen;
- protected ClassFile classfile;
+ ClassPool classPool;
+ boolean wasChanged;
+ boolean wasFrozen;
+ ClassFile classfile;
private CtField fieldsCache;
private CtConstructor constructorsCache;
@@ -127,6 +127,8 @@ class CtClassType extends CtClass {
public ClassPool getClassPool() { return classPool; }
+ void setClassPool(ClassPool cp) { classPool = cp; }
+
public URL getURL() throws NotFoundException {
URL url = classPool.find(getName());
if (url == null)
@@ -762,23 +764,25 @@ class CtClassType extends CtClass {
}
}
- void toBytecode(DataOutputStream out)
+ public void toBytecode(DataOutputStream out)
throws CannotCompileException, IOException
{
- ClassFile cf = getClassFile2();
try {
- modifyClassConstructor(cf);
- modifyConstructors(cf);
+ if (isModified()) {
+ ClassFile cf = getClassFile2();
+ modifyClassConstructor(cf);
+ modifyConstructors(cf);
+ cf.write(out);
+ out.flush();
+ }
+ else
+ classPool.writeClassfile(getName(), out);
+
+ wasFrozen = true;
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
-
- wasFrozen = true;
- try {
- cf.write(out);
- out.flush();
- }
catch (IOException e) {
throw new CannotCompileException(e);
}
diff --git a/src/main/javassist/CtNewClass.java b/src/main/javassist/CtNewClass.java
index a25540e9..c0acc9d5 100644
--- a/src/main/javassist/CtNewClass.java
+++ b/src/main/javassist/CtNewClass.java
@@ -55,7 +55,7 @@ class CtNewClass extends CtClassType {
super.addConstructor(c);
}
- void toBytecode(DataOutputStream out)
+ public void toBytecode(DataOutputStream out)
throws CannotCompileException, IOException
{
if (!hasConstructor)
diff --git a/src/main/javassist/Loader.java b/src/main/javassist/Loader.java
index ad3033aa..a8e80230 100644
--- a/src/main/javassist/Loader.java
+++ b/src/main/javassist/Loader.java
@@ -44,8 +44,9 @@ import java.util.Vector;
* public class Main {
* public static void main(String[] args) throws Throwable {
* MyTranslator myTrans = new MyTranslator();
- * ClassPool cp = ClassPool.getDefault(myTrans);
+ * ClassPool cp = ClassPool.getDefault();
* Loader cl = new Loader(cp);
+ * cl.addTranslator(cp, myTrans);
* cl.run("MyApp", args);
* }
* }
@@ -130,6 +131,7 @@ public class Loader extends ClassLoader {
private Hashtable notDefinedHere; // must be atomic.
private Vector notDefinedPackages; // must be atomic.
private ClassPool source;
+ private Translator translator;
/**
* Specifies the algorithm of class loading.
@@ -176,6 +178,7 @@ public class Loader extends ClassLoader {
notDefinedHere = new Hashtable();
notDefinedPackages = new Vector();
source = cp;
+ translator = null;
delegateLoadingOf("javassist.Loader");
}
@@ -202,6 +205,21 @@ public class Loader extends ClassLoader {
}
/**
+ * Adds a translator, which is called whenever a class is loaded.
+ *
+ * @param cp the <code>ClassPool</code> object for obtaining
+ * a class file.
+ * @param t a translator.
+ */
+ public void addTranslator(ClassPool cp, Translator t)
+ throws NotFoundException, CannotCompileException
+ {
+ source = cp;
+ translator = t;
+ t.start(cp);
+ }
+
+ /**
* Loads a class with an instance of <code>Loader</code>
* and calls <code>main()</code> of that class.
*
@@ -291,8 +309,13 @@ public class Loader extends ClassLoader {
protected Class findClass(String name) {
byte[] classfile;
try {
- if (source != null)
- classfile = source.write(name);
+ if (source != null) {
+ CtClass c = source.get(name);
+ if (translator != null)
+ translator.onWrite(source, c);
+
+ classfile = c.toBytecode();
+ }
else {
String jarname = "/" + name.replace('.', '/') + ".class";
InputStream in = this.getClass().getResourceAsStream(jarname);
@@ -369,7 +392,7 @@ public class Loader extends ClassLoader {
protected Package getPackage(String name) {
return super.getPackage(name);
}
- /*
+ /*
// Package p = super.getPackage(name);
Package p = null;
if (p == null)
diff --git a/src/main/javassist/Translator.java b/src/main/javassist/Translator.java
index 57cca039..8380fe8a 100644
--- a/src/main/javassist/Translator.java
+++ b/src/main/javassist/Translator.java
@@ -16,45 +16,35 @@
package javassist;
/**
- * An observer of <code>ClassPool</code>.
+ * An observer of <code>Loader</code>.
* The users can define a class implementing this
* interface and attach an instance of that class to a
- * <code>ClassPool</code> object so that it can translate a class file
- * when the class file is loaded into the JVM, for example.
+ * <code>Loader</code> object so that it can translate a class file
+ * when the class file is loaded into the JVM.
*
- * @see ClassPool#ClassPool(ClassPool,Translator)
- * @see ClassPool#getDefault(Translator)
+ * @see Loader
*/
public interface Translator {
/**
- * Is invoked by a <code>ClassPool</code> for initialization
- * when the object is attached to a <code>ClassPool</code> object.
+ * Is invoked by a <code>Loader</code> for initialization
+ * when the object is attached to the <code>Loader</code> object.
*
* @param pool the <code>ClassPool</code> that this translator
- * is attached to.
- *
- * @see ClassPool#ClassPool(ClassPool,Translator)
- * @see ClassPool#getDefault(Translator)
+ * should use.
+ * @see Loader
*/
void start(ClassPool pool)
throws NotFoundException, CannotCompileException;
/**
- * Is invoked by a <code>ClassPool</code> for notifying that
- * a class is written out to an output stream.
- *
- * <p>If <code>CtClass.frozen()</code> is true, that is, if the class has been
- * already modified and written, then onWrite() is not invoked.
+ * Is invoked by a <code>Loader</code> for notifying that
+ * a class is loaded.
*
* @param pool the <code>ClassPool</code> that this translator
- * is attached to.
- * @param classname a fully-qualified class name
- *
- * @see ClassPool#writeFile(String)
- * @see ClassPool#writeFile(String, String)
- * @see ClassPool#write(String)
- * @see ClassPool#write(String,DataOutputStream)
+ * should use.
+ * @param clazz the class that is being loaded.
+ * @see Loader
*/
- void onWrite(ClassPool pool, String classname)
+ void onWrite(ClassPool pool, CtClass clazz)
throws NotFoundException, CannotCompileException;
}
diff --git a/src/main/javassist/reflect/Compiler.java b/src/main/javassist/reflect/Compiler.java
index 222683a6..6ab55583 100644
--- a/src/main/javassist/reflect/Compiler.java
+++ b/src/main/javassist/reflect/Compiler.java
@@ -91,7 +91,7 @@ public class Compiler {
{
Reflection implementor = new Reflection();
ClassPool pool = ClassPool.getDefault();
- pool.addTranslator(implementor);
+ implementor.start(pool);
for (int i = 0; i < n; ++i) {
CtClass c = pool.get(entries[i].classname);
@@ -121,8 +121,11 @@ public class Compiler {
System.err.println(c.getName() + ": not reflective");
}
- for (int i = 0; i < n; ++i)
- pool.writeFile(entries[i].classname);
+ for (int i = 0; i < n; ++i) {
+ CtClass c = pool.get(entries[i].classname);
+ implementor.onWrite(pool, c);
+ c.writeFile();
+ }
}
private static int parse(String[] args, CompiledClass[] result) {
diff --git a/src/main/javassist/reflect/Loader.java b/src/main/javassist/reflect/Loader.java
index 626c329e..68381956 100644
--- a/src/main/javassist/reflect/Loader.java
+++ b/src/main/javassist/reflect/Loader.java
@@ -128,14 +128,13 @@ public class Loader extends javassist.Loader {
/**
* Constructs a new class loader.
*/
- public Loader() {
+ public Loader() throws CannotCompileException, NotFoundException {
super();
delegateLoadingOf("javassist.reflect.Loader");
reflection = new Reflection();
ClassPool pool = ClassPool.getDefault();
- pool.addTranslator(reflection);
- setClassPool(pool);
+ addTranslator(pool, reflection);
}
/**
diff --git a/src/main/javassist/reflect/Reflection.java b/src/main/javassist/reflect/Reflection.java
index b199a7ec..44477ee9 100644
--- a/src/main/javassist/reflect/Reflection.java
+++ b/src/main/javassist/reflect/Reflection.java
@@ -21,9 +21,6 @@ import javassist.CtMethod.ConstParameter;
/**
* The class implementing the reflection mechanism.
*
- * <p>This class is used with <code>ClassPool</code>.
- * Note that it implements an interface <code>javassist.Translator</code>.
- *
* <p>If a class is reflective,
* then all the method invocations on every
* instance of that class are intercepted by the runtime
@@ -58,8 +55,6 @@ import javassist.CtMethod.ConstParameter;
* @see javassist.reflect.Metaobject
* @see javassist.reflect.Loader
* @see javassist.reflect.Compiler
- * @see javassist.ClassPool
- * @see javassist.Translator
*/
public class Reflection implements Translator {
@@ -120,11 +115,10 @@ public class Reflection implements Translator {
* Inserts hooks for intercepting accesses to the fields declared
* in reflective classes.
*/
- public void onWrite(ClassPool pool, String classname)
+ public void onWrite(ClassPool pool, CtClass clazz)
throws CannotCompileException, NotFoundException
{
- CtClass c = pool.get(classname);
- c.instrument(converter);
+ clazz.instrument(converter);
}
/**
diff --git a/src/main/javassist/rmi/AppletServer.java b/src/main/javassist/rmi/AppletServer.java
index 04628516..25658e59 100644
--- a/src/main/javassist/rmi/AppletServer.java
+++ b/src/main/javassist/rmi/AppletServer.java
@@ -82,8 +82,7 @@ public class AppletServer extends Webserver {
exportedNames = new Hashtable();
exportedObjects = new Vector();
stubGen = gen;
- loader.addTranslator(gen);
- setClassPool(loader);
+ addTranslator(loader, gen);
}
/**
diff --git a/src/main/javassist/rmi/StubGenerator.java b/src/main/javassist/rmi/StubGenerator.java
index 3a030a23..638acd45 100644
--- a/src/main/javassist/rmi/StubGenerator.java
+++ b/src/main/javassist/rmi/StubGenerator.java
@@ -82,7 +82,7 @@ public class StubGenerator implements Translator {
= new CtClass[] { pool.get("javassist.rmi.RemoteException") };
}
- public void onWrite(ClassPool pool, String classname) {}
+ public void onWrite(ClassPool pool, CtClass clazz) {}
/**
* Returns <code>true</code> if the specified class is a proxy class
diff --git a/src/main/javassist/web/Webserver.java b/src/main/javassist/web/Webserver.java
index a6ef277c..120646e2 100644
--- a/src/main/javassist/web/Webserver.java
+++ b/src/main/javassist/web/Webserver.java
@@ -18,7 +18,7 @@ package javassist.web;
import java.net.*;
import java.io.*;
import java.util.Date;
-import javassist.ClassPool;
+import javassist.*;
/**
* A web server for Javassist.
@@ -28,9 +28,6 @@ import javassist.ClassPool;
* does not allow an applet to create and use a class loader,
* instrumenting class files must be done by this web server.
*
- * <p>Programmers can register a <code>ClassPool</code> object for
- * instrumenting class files when they are sent to web browsers.
- *
* <p><b>Note:</b> although this class is included in the Javassist API,
* it is provided as a sample implementation of the web server using
* Javassist. Especially, there might be security flaws in this server.
@@ -39,6 +36,7 @@ import javassist.ClassPool;
public class Webserver {
private ServerSocket socket;
private ClassPool classPool;
+ protected Translator translator;
private final static byte[] endofline = { 0x0d, 0x0a };
private byte[] filebuffer = new byte[4096];
@@ -105,6 +103,7 @@ public class Webserver {
public Webserver(int port) throws IOException {
socket = new ServerSocket(port);
classPool = null;
+ translator = null;
}
/**
@@ -116,6 +115,22 @@ public class Webserver {
}
/**
+ * Adds a translator, which is called whenever a client requests
+ * a class file.
+ *
+ * @param cp the <code>ClassPool</code> object for obtaining
+ * a class file.
+ * @param t a translator.
+ */
+ public void addTranslator(ClassPool cp, Translator t)
+ throws NotFoundException, CannotCompileException
+ {
+ classPool = cp;
+ translator = t;
+ t.start(classPool);
+ }
+
+ /**
* Closes the socket.
*/
public void end() throws IOException {
@@ -322,9 +337,13 @@ public class Webserver {
String classname
= filename.substring(0, length - 6).replace('/', '.');
try {
- classfile = classPool.write(classname);
+ CtClass c = classPool.get(classname);
+ if (translator != null)
+ translator.onWrite(classPool, c);
+
+ classfile = c.toBytecode();
if (debugDir != null)
- classPool.writeFile(classname, debugDir);
+ c.writeFile(debugDir);
}
catch (Exception e) {
throw new BadHttpRequest(e);