Browse Source

modified the ClassPool framework.


git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@84 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
tags/rel_3_17_1_ga
chiba 20 years ago
parent
commit
3f73777f2c

+ 6
- 1
Readme.html View File

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

+ 1
- 1
sample/Test.java View File

@@ -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.");
}
}

+ 1
- 2
sample/evolve/DemoLoader.java View File

@@ -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);

+ 2
- 3
sample/evolve/Evolution.java View File

@@ -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);

+ 2
- 2
sample/vector/VectorAssistant.java View File

@@ -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;
}

+ 130
- 367
src/main/javassist/ClassPool.java View File

@@ -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,61 +110,23 @@ 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
@@ -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.
@@ -231,14 +206,6 @@ public class ClassPool extends AbsClassPool {
source.recordInvalidClassName(name);
}

/**
* 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,186 +259,35 @@ 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);
}
}

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

+ 93
- 59
src/main/javassist/ClassPoolTail.java View File

@@ -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;
@@ -272,6 +216,65 @@ final class ClassPoolTail extends AbsClassPool {
return new DirClassPath(pathname);
}

/**
* 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");
}
}

+ 129
- 37
src/main/javassist/CtClass.java View File

@@ -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");

+ 18
- 14
src/main/javassist/CtClassType.java View File

@@ -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);
}

+ 1
- 1
src/main/javassist/CtNewClass.java View File

@@ -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)

+ 27
- 4
src/main/javassist/Loader.java View File

@@ -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");
}

@@ -201,6 +204,21 @@ public class Loader extends ClassLoader {
source = cp;
}

/**
* 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)

+ 14
- 24
src/main/javassist/Translator.java View File

@@ -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;
}

+ 6
- 3
src/main/javassist/reflect/Compiler.java View File

@@ -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) {

+ 2
- 3
src/main/javassist/reflect/Loader.java View File

@@ -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);
}

/**

+ 2
- 8
src/main/javassist/reflect/Reflection.java View File

@@ -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);
}

/**

+ 1
- 2
src/main/javassist/rmi/AppletServer.java View File

@@ -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);
}

/**

+ 1
- 1
src/main/javassist/rmi/StubGenerator.java View File

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

+ 25
- 6
src/main/javassist/web/Webserver.java View File

@@ -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;
}

/**
@@ -115,6 +114,22 @@ public class Webserver {
classPool = loader;
}

/**
* 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.
*/
@@ -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);

Loading…
Cancel
Save