Browse Source

Changed the implementation of CtClass#toClass() and fixed [Bugs item #993105] Reflection#makeReflective could reject some reflections


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

+ 6
- 0
Readme.html View File

@@ -255,6 +255,12 @@ see javassist.Dump.

<h2>Changes</h2>

<p>- version 3.0

<ul>
<li>CtClass.toClass() has been reimplemented.
</ul>

<p>- version 3.0 beta in May 18th, 2004.

<ul>

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

@@ -41,7 +41,7 @@ public class CannotCompileException extends Exception {
/**
* Constructs a CannotCompileException with an <code>Exception</code>.
*/
public CannotCompileException(Exception e) {
public CannotCompileException(Throwable e) {
super("by " + e.toString());
message = null;
}

+ 0
- 81
src/main/javassist/ClassPool.java View File

@@ -219,87 +219,6 @@ public class ClassPool {
return (Object[])cflow.get(name);
}

/**
* 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 CtClass#forName(String)
* @see CtClass#toClass()
* @see ClassPool#toClass(CtClass)
* @see ClassPool#forName(String)
*/
public static class SimpleLoader extends ClassLoader {
/**
* Loads a class.
*
* @param name the fully qualified class name.
* @param classfile the class file.
* @throws ClassFormatError if the class file is wrong.
*/
public Class loadClass(String name, byte[] classfile)
throws ClassFormatError
{
Class c = defineClass(name, classfile, 0, classfile.length);
resolveClass(c);
return c;
}
};

private static SimpleLoader classLoader = null;

/**
* Returns a <code>java.lang.Class</code> object that has been loaded
* by <code>toClass()</code> in <code>CtClass</code>
* or <code>ClassPool</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.
*
* @param name the fully-qualified class name.
* @see #toClass(CtClass)
*/
public Class forName(String name) throws ClassNotFoundException {
if (classLoader == null)
classLoader = new SimpleLoader();

return classLoader.loadClass(name);
}

/**
* Loads the class represented by a <code>CtClass</code> object.
* <code>toClass()</code> in <code>CtClass</code> calls this method
* to load a class.
*
* <p>This method can be overridden to change the class loader.
* In the default implementation,
* the class loader is a <code>SimpleLoader</code>.
* If this method is overridden, <code>forName()</code> must be also
* overridden for extending the behavior.
*
* @param cc the loaded <code>CtClass</code>.
* @return the <code>java.lang.Class</code> object representing
* the loaded class.
* @see #forName(String)
*/
public Class toClass(CtClass cc)
throws NotFoundException, CannotCompileException, IOException
{
if (cc.getClassPool() != this)
throw new CannotCompileException(
"not contained in this class pool: " + cc.getName());

try {
if (classLoader == null)
classLoader = new SimpleLoader();

return classLoader.loadClass(cc.getName(), cc.toBytecode());
}
catch (ClassFormatError e) {
throw new CannotCompileException(e, cc.getName());
}
}

/**
* Reads a class file and constructs a <code>CtClass</code>
* object with a new name.

+ 48
- 40
src/main/javassist/CtClass.java View File

@@ -843,57 +843,65 @@ public abstract class CtClass {
/**
* Converts this class to a <code>java.lang.Class</code> object.
* Once this method is called, further modifications are not
* possible any more.
* allowed any more.
* To load the class, this method uses the context class loader
* of the current thread. If the program is running on some application
* server, the context class loader might be inappropriate to load the
* class.
*
* <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>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.SimpleLoader
* @see Loader
* @see #toClass(java.lang.ClassLoader)
*/
public Class toClass()
throws NotFoundException, IOException, CannotCompileException
throws CannotCompileException
{
return getClassPool().toClass(this);
return toClass(Thread.currentThread().getContextClassLoader());
}

/**
* 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.
* Converts this class to a <code>java.lang.Class</code> object.
* Once this method is called, further modifications are not allowed
* any more.
*
* <p>The class file represented by this <code>CtClass</code> is
* loaded by the given class loader to construct a
* <code>java.lang.Class</code> object. Since a private method
* on the class loader is invoked through the reflection API,
* the caller must have permissions to do that.
*
* @see CtClass#toClass()
* @see ClassPool.SimpleLoader
* <p>This method is provided for convenience. If you need more
* complex functionality, you should write your own class loader.
*
* @param loader the class loader used to load this class.
*/
public Class forName(String name) throws ClassNotFoundException {
return getClassPool().forName(name);
public Class toClass(ClassLoader loader)
throws CannotCompileException
{
try {
byte[] b = toBytecode();
Class cl = Class.forName("java.lang.ClassLoader");
java.lang.reflect.Method method
= cl.getDeclaredMethod("defineClass",
new Class[] { String.class, byte[].class,
int.class, int.class });
method.setAccessible(true);
Object[] args = new Object[] { getName(), b, new Integer(0),
new Integer(b.length)};
Class clazz = (Class)method.invoke(loader, args);
method.setAccessible(false);
return clazz;
}
catch (RuntimeException e) {
throw e;
}
catch (java.lang.reflect.InvocationTargetException e) {
throw new CannotCompileException(e.getTargetException());
}
catch (Exception e) {
throw new CannotCompileException(e);
}
}

/**
@@ -921,7 +929,7 @@ public abstract class CtClass {
* @return the contents of the class file.
*/
public byte[] toBytecode()
throws NotFoundException, IOException, CannotCompileException
throws IOException, CannotCompileException
{
ByteArrayOutputStream barray = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(barray);

+ 34
- 0
src/main/javassist/reflect/CannotReflectException.java View File

@@ -0,0 +1,34 @@
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2004 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/

package javassist.reflect;

import javassist.CannotCompileException;

/**
* Thrown by <code>makeReflective()</code> in <code>Reflection</code>
* when there is an attempt to reflect
* a class that is either an interface or a subclass of
* either ClassMetaobject or Metaobject.
*
* @author Brett Randall
* @see javassist.reflect.Reflection#makeReflective(CtClass,CtClass,CtClass)
* @see javassist.CannotCompileException
*/
public class CannotReflectException extends CannotCompileException {
public CannotReflectException(String msg) {
super(msg);
}
}

+ 20
- 1
src/main/javassist/reflect/Reflection.java View File

@@ -66,6 +66,10 @@ public class Reflection implements Translator {
static final String readPrefix = "_r_";
static final String writePrefix = "_w_";

static final String metaobjectClassName = "javassist.reflect.Metaobject";
static final String classMetaobjectClassName
= "javassist.reflect.ClassMetaobject";

protected CtMethod trapMethod, trapStaticMethod;
protected CtMethod trapRead, trapWrite;
protected CtClass[] readParam;
@@ -189,8 +193,23 @@ public class Reflection implements Translator {
*/
public boolean makeReflective(CtClass clazz,
CtClass metaobject, CtClass metaclass)
throws CannotCompileException, NotFoundException
throws CannotCompileException, CannotReflectException,
NotFoundException
{
if (clazz.isInterface())
throw new CannotReflectException(
"Cannot reflect an interface: " + clazz.getName());

if (clazz.subclassOf(classPool.get(classMetaobjectClassName)))
throw new CannotReflectException(
"Cannot reflect a subclass of ClassMetaobject: "
+ clazz.getName());

if (clazz.subclassOf(classPool.get(metaobjectClassName)))
throw new CannotReflectException(
"Cannot reflect a subclass of Metaobject: "
+ clazz.getName());

registerReflectiveClass(clazz);
return modifyClassfile(clazz, metaobject, metaclass);
}

+ 122
- 64
tutorial/tutorial.html View File

@@ -393,39 +393,139 @@ modified at load time. The users of Javassist can define their own
version of class loader but they can also use a class loader provided
by Javassist.

<p>Using a class loader is not easy. In particular, if you are a
beginner, you should separate your program into an application program
and an instrumentation program. Then you should load only the former
program by a user-defined class loader. The latter one, as well as
the program of the user-defined class loader, should be loaded by the
system class loader.

<ul>
<b>Note:</b> The JVM does not allow dynamically reloading a class.
Once a class loader loads a class, it cannot reload a modified
version of that class during runtime. Thus, you cannot alter
the definition of a class after the JVM loads it.
However, the JPDA (Java Platform Debugger Architecture) provides
limited ability for reloading a class. See "HotSwap" of JPDA for details.
</ul>
<p><br>

<h3>4.1 The <code>toClass</code> method in <code>CtClass</code></h3>

<p>The <code>CtClass</code> provides a convenience method
<code>toClass()</code>, which requests the context class loader for
the current thread to load the class represented by the <code>CtClass</code>
object. To call this method, the caller must have appropriate permission;
otherwise, a <code>SecurityException</code> may be thrown.

<p>The following program shows how to use <code>toClass()</code>:

<ul><pre>
public class Hello {
public void say() {
System.out.println("Hello");
}
}

public class Test {
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("Hello");
CtMethod m = cc.getDeclaredMethod("say");
m.insertBefore("{ System.out.println(\"Hello.say():\"); }");
Class c = cc.toClass();
Hello h = (Hello)c.newInstance();
h.say();
}
}
</pre></ul>

<p><code>Test.main()</code> inserts a call to <code>println()</code>
in the method body of <code>say()</code> in <code>Hello</code>. Then
it constructs an instance of the modified <code>Hello</code> class
and calls <code>say()</code> on that instance.

<p>Note that the program above depends on the fact that the
<code>Hello</code> class is never loaded before <code>toClass()</code>
is invoked. If not, the JVM would load the original
<code>Hello</code> class before <code>toClass()</code> request to load
the modified <code>Hello</code> class. Hence loading the modified
<code>Hello</code> class would be failed.
For example, if
<code>main()</code> in <code>Test</code> is something like this:

<ul><pre>
public static void main(String[] args) throws Exception {
Hello orig = new Hello();
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("Hello");
:
}
</pre></ul>

<p>then the original <code>Hello</code> class is loaded at the first
line of <code>main</code> and the call to <code>toClass()</code>
throws an exception since the class loader cannot load two different
versions of the <code>Hello</code> class at the same time.

<p><em>If the program is running on some application server such as
JBoss,</em> the context class loader used by <code>toClass()</code>
might be inappropriate. In this case, you would see an unexpected
<code>ClassCastException</code>. To avoid this exception, you must
explicitly give an appropriate class loader to <code>toClass()</code>.
For example, if <code>bean</code> is your session bean object, then the
following code:

<ul><pre>CtClass cc = ...;
Class c = cc.toClass(bean.getClass().getClassLoader());
</pre></ul>

<p>would work. You should give <code>toClass()</code> the class loader
that has loaded your program (in the above example, the class of
the <code>bean</code> object).

<p><code>toClass()</code> is provided for convenience. If you need
more complex functionality, you should write your own class loader.

<p><br>

<h3>4.1 Class loading in Java</h3>
<h3>4.2 Class loading in Java</h3>

<p>In Java, multiple class loaders can coexist and
each class loader creates its own name space.
Different class loaders can load different class files with the
same class name. The loaded two classes are regarded as different
ones. This feature enables us to run multiple application programs
on a single JVM.
on a single JVM even if these programs include different classes
with the same name.

<ul>
<b>Note:</b> The JVM does not allow dynamically reloading a class.
Once a class loader loads a class, it cannot reload a modified
version of that class during runtime. Thus, you cannot alter
the definition of a class after the JVM loads it.
However, the JPDA (Java Platform Debugger Architecture) provides
limited ability for reloading a class. See "HotSwap" of JPDA for details.
</ul>

<p>If the same class file is loaded by two distinct class loaders,
the JVM makes two distinct classes with the same name and definition.
The two classes are regarded as different ones.
Since the two classes are not identical, an instance of one class is
not assignable to a variable of the other class. The cast operation
between the two classes fails and throws a <code>ClassCastException</code>.
between the two classes fails
and throws a <em><code>ClassCastException</code></em>.

<p>For example, the following code snippet throws an exception:

<ul><pre>
MyClassLoader myLoader = new MyClassLoader();
Class clazz = myLoader.loadClass("Box");
Object obj = clazz.newInstance();
Box b = (Box)obj; // this always throws ClassCastException.
</pre></ul>

<p>
The <code>Box</code> class is loaded by two class loaders.
Suppose that a class loader CL loads a class including this code snippet.
Since this code snippet refers to <code>MyClassLoader</code>,
<code>Class</code>, <code>Object</code>, and <code>Box</code>,
CL also loads these classes (unless it delegates to another class loader).
Hence the type of the variable <code>b</code> is the <code>Box</code>
class loaded by CL.
On the other hand, <code>myLoader</code> also loads the <code>Box</code>
class. The object <code>obj</code> is an instance of
the <code>Box</code> class loaded by <code>myLoader</code>.
Therefore, the last statement always throws a
<code>ClassCastException</code> since the class of <code>obj</code> is
a different verison of the <code>Box</code> class from one used as the
type of the variable <code>b</code>.

<p>Multiple class loaders form a tree structure.
Each class loader except the bootstrap loader has a
@@ -555,7 +655,7 @@ be helpful:

<p><br>

<h3>4.2 Using <code>javassist.Loader</code></h3>
<h3>4.3 Using <code>javassist.Loader</code></h3>

<p>Javassist provides a class loader
<code>javassist.Loader</code>. This class loader uses a
@@ -648,7 +748,7 @@ public class Main2 {
<p>To run this program, do:

<ul><pre>
% java Main <i>arg1</i> <i>arg2</i>...
% java Main2 <i>arg1</i> <i>arg2</i>...
</pre></ul>

<p>The class <code>MyApp</code> and the other application classes
@@ -656,10 +756,10 @@ are translated by <code>MyTranslator</code>.

<p>Note that <em>application</em> classes like <code>MyApp</code> cannot
access the <em>loader</em> classes such as <code>Main2</code>,
<code>MyTranslator</code> and <code>ClassPool</code> because they
<code>MyTranslator</code>, and <code>ClassPool</code> because they
are loaded by different loaders. The application classes are loaded
by <code>javassist.Loader</code> whereas the loader classes such as
<code>Main</code> are by the default Java class loader.
<code>Main2</code> are by the default Java class loader.

<p><code>javassist.Loader</code> searches for classes in a different
order from <code>java.lang.ClassLoader</code>.
@@ -692,7 +792,7 @@ make sure whether all the classes using that class have been loaded by

<p><br>

<h3>4.3 Writing a class loader</h3>
<h3>4.4 Writing a class loader</h3>

<p>A simple class loader using Javassist is as follows:

@@ -763,48 +863,6 @@ Hence, the

<p><br>

<h3>4.4 The <code>toClass</code> method in <code>CtClass</code></h3>

<p>The <code>CtClass</code> provides a convenience method
<code>toClass</code>, which loads the class by an internal class
loader of Javassist. This method first obtains the class file
representing the modified class and loads it by an instance of
<code>javassist.ClassPool.SimpleLoader</code>.
The following code is the definition of this class loader:

<ul><pre>
public class SimpleLoader extends ClassLoader {
public Class loadClass(String classname, byte[] classfile)
throws ClassFormatError
{
Class c = defineClass(classname, classfile, 0, classfile.length);
resolveClass(c);
return c;
}
};
</pre></ul>

<p><code>loadClass()</code> loads the class specified by
<code>classfile</code>.
Thus, <code>toClass()</code> is equivalent to the following code:

<ul><pre>
CtClass cc = ... ;
ClassPool.SimpleLoader cl = new ClassPool.SimpleLoader();
Class c = cl.loadClass(cc.getName(), cc.toBytecode());
</pre></ul>

<p>Note that this class loader might be too simple for realistic use.
It delegates to the parent class loader unless the class is explicitly
loaded by <code>loadClass()</code> (or <code>toClass()</code> in
<code>CtClass</code>). If you encounter an unexpected
<code>ClassCastException</code>, you should check the class loader of
the object. Call <code>getClass().getClassLoader()</code> on the
object and make sure that the destination class and the source class
of the cast operation have been loaded by the same class loader.

<p><br>

<h3>4.5 Modifying a system class</h3>

<p>The system classes like <code>java.lang.String</code> cannot be

Loading…
Cancel
Save