You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
433 lines
14 KiB
433 lines
14 KiB
/* |
|
* Javassist, a Java-bytecode translator toolkit. |
|
* Copyright (C) 1999- 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, |
|
* or the Apache License Version 2.0. |
|
* |
|
* 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 com.fr.third.javassist; |
|
|
|
import java.io.*; |
|
import java.util.Hashtable; |
|
import java.util.Vector; |
|
import java.security.ProtectionDomain; |
|
|
|
/** |
|
* The class loader for Javassist. |
|
* |
|
* <p>This is a sample class loader using <code>ClassPool</code>. |
|
* Unlike a regular class loader, this class loader obtains bytecode |
|
* from a <code>ClassPool</code>. |
|
* |
|
* <p>Note that Javassist can be used without this class loader; programmers |
|
* can define their own versions of class loader. They can run |
|
* a program even without any user-defined class loader if that program |
|
* is statically translated with Javassist. |
|
* This class loader is just provided as a utility class. |
|
* |
|
* <p>Suppose that an instance of <code>MyTranslator</code> implementing |
|
* the interface <code>Translator</code> is responsible for modifying |
|
* class files. |
|
* The startup program of an application using <code>MyTranslator</code> |
|
* should be something like this: |
|
* |
|
* <ul><pre> |
|
* import javassist.*; |
|
* |
|
* public class Main { |
|
* public static void main(String[] args) throws Throwable { |
|
* MyTranslator myTrans = new MyTranslator(); |
|
* ClassPool cp = ClassPool.getDefault(); |
|
* Loader cl = new Loader(cp); |
|
* cl.addTranslator(cp, myTrans); |
|
* cl.run("MyApp", args); |
|
* } |
|
* } |
|
* </pre></ul> |
|
* |
|
* <p>Class <code>MyApp</code> is the main program of the application. |
|
* |
|
* <p>This program should be executed as follows: |
|
* |
|
* <ul><pre> |
|
* % java Main <i>arg1</i> <i>arg2</i>... |
|
* </pre></ul> |
|
* |
|
* <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code> |
|
* object before the JVM loads it. |
|
* Then it calls <code>main()</code> in <code>MyApp</code> with arguments |
|
* <i>arg1</i>, <i>arg2</i>, ... |
|
* |
|
* <p>This program execution is equivalent to: |
|
* |
|
* <ul><pre> |
|
* % java MyApp <i>arg1</i> <i>arg2</i>... |
|
* </pre></ul> |
|
* |
|
* <p>except that classes are translated by <code>MyTranslator</code> |
|
* at load time. |
|
* |
|
* <p>If only a particular class must be modified when it is loaded, |
|
* the startup program can be simpler; <code>MyTranslator</code> is |
|
* unnecessary. For example, if only a class <code>test.Rectangle</code> |
|
* is modified, the <code>main()</code> method above will be the following: |
|
* |
|
* <ul><pre> |
|
* ClassPool cp = ClassPool.getDefault(); |
|
* Loader cl = new Loader(cp); |
|
* CtClass ct = cp.get("test.Rectangle"); |
|
* ct.setSuperclass(cp.get("test.Point")); |
|
* cl.run("MyApp", args);</pre></ul> |
|
* |
|
* <p>This program changes the super class of the <code>test.Rectangle</code> |
|
* class. |
|
* |
|
* <p><b>Note 1:</b> |
|
* |
|
* <p>This class loader does not allow the users to intercept the loading |
|
* of <code>java.*</code> and <code>javax.*</code> classes (and |
|
* <code>sun.*</code>, <code>org.xml.*</code>, ...) unless |
|
* <code>Loader.doDelegation</code> is <code>false</code>. This is because |
|
* the JVM prohibits a user class loader from loading a system class. |
|
* Also see Note 2. |
|
* If this behavior is not appropriate, a subclass of <code>Loader</code> |
|
* must be defined and <code>loadClassByDelegation()</code> must be overridden. |
|
* |
|
* <p><b>Note 2:</b> |
|
* |
|
* <p>If classes are loaded with different class loaders, they belong to |
|
* separate name spaces. If class <code>C</code> is loaded by a class |
|
* loader <code>CL</code>, all classes that the class <code>C</code> |
|
* refers to are also loaded by <code>CL</code>. However, if <code>CL</code> |
|
* delegates the loading of the class <code>C</code> to <code>CL'</code>, |
|
* then those classes that the class <code>C</code> refers to |
|
* are loaded by a parent class loader <code>CL'</code> |
|
* instead of <code>CL</code>. |
|
* |
|
* <p>If an object of class <code>C</code> is assigned |
|
* to a variable of class <code>C</code> belonging to a different name |
|
* space, then a <code>ClassCastException</code> is thrown. |
|
* |
|
* <p>Because of the fact above, this loader delegates only the loading of |
|
* <code>javassist.Loader</code> |
|
* and classes included in package <code>java.*</code> and |
|
* <code>javax.*</code> to the parent class |
|
* loader. Other classes are directly loaded by this loader. |
|
* |
|
* <p>For example, suppose that <code>java.lang.String</code> would be loaded |
|
* by this loader while <code>java.io.File</code> is loaded by the parent |
|
* class loader. If the constructor of <code>java.io.File</code> is called |
|
* with an instance of <code>java.lang.String</code>, then it may throw |
|
* an exception since it accepts an instance of only the |
|
* <code>java.lang.String</code> loaded by the parent class loader. |
|
* |
|
* @see com.fr.third.javassist.ClassPool |
|
* @see Translator |
|
*/ |
|
public class Loader extends ClassLoader { |
|
private Hashtable notDefinedHere; // must be atomic. |
|
private Vector notDefinedPackages; // must be atomic. |
|
private com.fr.third.javassist.ClassPool source; |
|
private Translator translator; |
|
private ProtectionDomain domain; |
|
|
|
/** |
|
* Specifies the algorithm of class loading. |
|
* |
|
* <p>This class loader uses the parent class loader for |
|
* <code>java.*</code> and <code>javax.*</code> classes. |
|
* If this variable <code>doDelegation</code> |
|
* is <code>false</code>, this class loader does not delegate those |
|
* classes to the parent class loader. |
|
* |
|
* <p>The default value is <code>true</code>. |
|
*/ |
|
public boolean doDelegation = true; |
|
|
|
/** |
|
* Creates a new class loader. |
|
*/ |
|
public Loader() { |
|
this(null); |
|
} |
|
|
|
/** |
|
* Creates a new class loader. |
|
* |
|
* @param cp the source of class files. |
|
*/ |
|
public Loader(com.fr.third.javassist.ClassPool cp) { |
|
init(cp); |
|
} |
|
|
|
/** |
|
* Creates a new class loader |
|
* using the specified parent class loader for delegation. |
|
* |
|
* @param parent the parent class loader. |
|
* @param cp the source of class files. |
|
*/ |
|
public Loader(ClassLoader parent, com.fr.third.javassist.ClassPool cp) { |
|
super(parent); |
|
init(cp); |
|
} |
|
|
|
private void init(com.fr.third.javassist.ClassPool cp) { |
|
notDefinedHere = new Hashtable(); |
|
notDefinedPackages = new Vector(); |
|
source = cp; |
|
translator = null; |
|
domain = null; |
|
delegateLoadingOf("javassist.Loader"); |
|
} |
|
|
|
/** |
|
* Records a class so that the loading of that class is delegated |
|
* to the parent class loader. |
|
* |
|
* <p>If the given class name ends with <code>.</code> (dot), then |
|
* that name is interpreted as a package name. All the classes |
|
* in that package and the sub packages are delegated. |
|
*/ |
|
public void delegateLoadingOf(String classname) { |
|
if (classname.endsWith(".")) |
|
notDefinedPackages.addElement(classname); |
|
else |
|
notDefinedHere.put(classname, this); |
|
} |
|
|
|
/** |
|
* Sets the protection domain for the classes handled by this class |
|
* loader. Without registering an appropriate protection domain, |
|
* the program loaded by this loader will not work with a security |
|
* manager or a signed jar file. |
|
*/ |
|
public void setDomain(ProtectionDomain d) { |
|
domain = d; |
|
} |
|
|
|
/** |
|
* Sets the soruce <code>ClassPool</code>. |
|
*/ |
|
public void setClassPool(com.fr.third.javassist.ClassPool cp) { |
|
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. |
|
* @throws NotFoundException if <code>t.start()</code> throws an exception. |
|
* @throws com.fr.third.javassist.CannotCompileException if <code>t.start()</code> throws an exception. |
|
*/ |
|
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. |
|
* |
|
* <p>This method calls <code>run()</code>. |
|
* |
|
* @param args command line parameters. |
|
* <ul> |
|
* <code>args[0]</code> is the class name to be loaded. |
|
* <br><code>args[1..n]</code> are parameters passed |
|
* to the target <code>main()</code>. |
|
* </ul> |
|
* |
|
* @see Loader#run(String[]) |
|
*/ |
|
public static void main(String[] args) throws Throwable { |
|
Loader cl = new Loader(); |
|
cl.run(args); |
|
} |
|
|
|
/** |
|
* Loads a class and calls <code>main()</code> in that class. |
|
* |
|
* @param args command line parameters. |
|
* <ul> |
|
* <code>args[0]</code> is the class name to be loaded. |
|
* <br><code>args[1..n]</code> are parameters passed |
|
* to the target <code>main()</code>. |
|
* </ul> |
|
*/ |
|
public void run(String[] args) throws Throwable { |
|
int n = args.length - 1; |
|
if (n >= 0) { |
|
String[] args2 = new String[n]; |
|
for (int i = 0; i < n; ++i) |
|
args2[i] = args[i + 1]; |
|
|
|
run(args[0], args2); |
|
} |
|
} |
|
|
|
/** |
|
* Loads a class and calls <code>main()</code> in that class. |
|
* |
|
* @param classname the loaded class. |
|
* @param args parameters passed to <code>main()</code>. |
|
*/ |
|
public void run(String classname, String[] args) throws Throwable { |
|
Class c = loadClass(classname); |
|
try { |
|
c.getDeclaredMethod("main", new Class[] { String[].class }).invoke( |
|
null, |
|
new Object[] { args }); |
|
} |
|
catch (java.lang.reflect.InvocationTargetException e) { |
|
throw e.getTargetException(); |
|
} |
|
} |
|
|
|
/** |
|
* Requests the class loader to load a class. |
|
*/ |
|
protected Class loadClass(String name, boolean resolve) |
|
throws ClassFormatError, ClassNotFoundException { |
|
name = name.intern(); |
|
synchronized (name) { |
|
Class c = findLoadedClass(name); |
|
if (c == null) |
|
c = loadClassByDelegation(name); |
|
|
|
if (c == null) |
|
c = findClass(name); |
|
|
|
if (c == null) |
|
c = delegateToParent(name); |
|
|
|
if (resolve) |
|
resolveClass(c); |
|
|
|
return c; |
|
} |
|
} |
|
|
|
/** |
|
* Finds the specified class using <code>ClassPath</code>. |
|
* If the source throws an exception, this returns null. |
|
* |
|
* <p>This method can be overridden by a subclass of |
|
* <code>Loader</code>. Note that the overridden method must not throw |
|
* an exception when it just fails to find a class file. |
|
* |
|
* @return null if the specified class could not be found. |
|
* @throws ClassNotFoundException if an exception is thrown while |
|
* obtaining a class file. |
|
*/ |
|
protected Class findClass(String name) throws ClassNotFoundException { |
|
byte[] classfile; |
|
try { |
|
if (source != null) { |
|
if (translator != null) |
|
translator.onLoad(source, name); |
|
|
|
try { |
|
classfile = source.get(name).toBytecode(); |
|
} |
|
catch (NotFoundException e) { |
|
return null; |
|
} |
|
} |
|
else { |
|
String jarname = "/" + name.replace('.', '/') + ".class"; |
|
InputStream in = this.getClass().getResourceAsStream(jarname); |
|
if (in == null) |
|
return null; |
|
|
|
classfile = com.fr.third.javassist.ClassPoolTail.readStream(in); |
|
} |
|
} |
|
catch (Exception e) { |
|
throw new ClassNotFoundException( |
|
"caught an exception while obtaining a class file for " |
|
+ name, e); |
|
} |
|
|
|
int i = name.lastIndexOf('.'); |
|
if (i != -1) { |
|
String pname = name.substring(0, i); |
|
if (getPackage(pname) == null) |
|
try { |
|
definePackage( |
|
pname, null, null, null, null, null, null, null); |
|
} |
|
catch (IllegalArgumentException e) { |
|
// ignore. maybe the package object for the same |
|
// name has been created just right away. |
|
} |
|
} |
|
|
|
if (domain == null) |
|
return defineClass(name, classfile, 0, classfile.length); |
|
else |
|
return defineClass(name, classfile, 0, classfile.length, domain); |
|
} |
|
|
|
protected Class loadClassByDelegation(String name) |
|
throws ClassNotFoundException |
|
{ |
|
/* The swing components must be loaded by a system |
|
* class loader. |
|
* javax.swing.UIManager loads a (concrete) subclass |
|
* of LookAndFeel by a system class loader and cast |
|
* an instance of the class to LookAndFeel for |
|
* (maybe) a security reason. To avoid failure of |
|
* type conversion, LookAndFeel must not be loaded |
|
* by this class loader. |
|
*/ |
|
|
|
Class c = null; |
|
if (doDelegation) |
|
if (name.startsWith("java.") |
|
|| name.startsWith("javax.") |
|
|| name.startsWith("sun.") |
|
|| name.startsWith("com.sun.") |
|
|| name.startsWith("org.w3c.") |
|
|| name.startsWith("org.xml.") |
|
|| notDelegated(name)) |
|
c = delegateToParent(name); |
|
|
|
return c; |
|
} |
|
|
|
private boolean notDelegated(String name) { |
|
if (notDefinedHere.get(name) != null) |
|
return true; |
|
|
|
int n = notDefinedPackages.size(); |
|
for (int i = 0; i < n; ++i) |
|
if (name.startsWith((String)notDefinedPackages.elementAt(i))) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
protected Class delegateToParent(String classname) |
|
throws ClassNotFoundException |
|
{ |
|
ClassLoader cl = getParent(); |
|
if (cl != null) |
|
return cl.loadClass(classname); |
|
else |
|
return findSystemClass(classname); |
|
} |
|
}
|
|
|