diff options
-rw-r--r-- | Readme.html | 7 | ||||
-rw-r--r-- | sample/Test.java | 2 | ||||
-rw-r--r-- | sample/evolve/DemoLoader.java | 3 | ||||
-rw-r--r-- | sample/evolve/Evolution.java | 5 | ||||
-rw-r--r-- | sample/vector/VectorAssistant.java | 4 | ||||
-rw-r--r-- | src/main/javassist/ClassPool.java | 497 | ||||
-rw-r--r-- | src/main/javassist/ClassPoolTail.java | 152 | ||||
-rw-r--r-- | src/main/javassist/CtClass.java | 166 | ||||
-rw-r--r-- | src/main/javassist/CtClassType.java | 32 | ||||
-rw-r--r-- | src/main/javassist/CtNewClass.java | 2 | ||||
-rw-r--r-- | src/main/javassist/Loader.java | 31 | ||||
-rw-r--r-- | src/main/javassist/Translator.java | 38 | ||||
-rw-r--r-- | src/main/javassist/reflect/Compiler.java | 9 | ||||
-rw-r--r-- | src/main/javassist/reflect/Loader.java | 5 | ||||
-rw-r--r-- | src/main/javassist/reflect/Reflection.java | 10 | ||||
-rw-r--r-- | src/main/javassist/rmi/AppletServer.java | 3 | ||||
-rw-r--r-- | src/main/javassist/rmi/StubGenerator.java | 2 | ||||
-rw-r--r-- | src/main/javassist/web/Webserver.java | 31 |
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); |