123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- /*
- * 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 javassist;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.lang.reflect.InvocationTargetException;
- import java.security.ProtectionDomain;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.Vector;
-
- import javassist.bytecode.ClassFile;
-
- /**
- * 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:
- *
- * <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>
- *
- * <p>Class <code>MyApp</code> is the main program of the application.
- *
- * <p>This program should be executed as follows:
- *
- * <pre>
- * % java Main <i>arg1</i> <i>arg2</i>...
- * </pre>
- *
- * <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:
- *
- * <pre>
- * % java MyApp <i>arg1</i> <i>arg2</i>...
- * </pre>
- *
- * <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:
- *
- * <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>
- *
- * <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 javassist.ClassPool
- * @see javassist.Translator
- */
- public class Loader extends ClassLoader {
-
- /**
- * A simpler class loader.
- * This is a class loader that exposes the protected {@code defineClass()} method
- * declared in {@code java.lang.ClassLoader}. It provides a method similar to
- * {@code CtClass#toClass()}.
- *
- * <p>When loading a class, this class loader delegates the work to the
- * parent class loader unless the loaded classes are explicitly given
- * by {@link #invokeDefineClass(CtClass)}.
- * Note that a class {@code Foo} loaded by this class loader is
- * different from the class with the same name {@code Foo} but loaded by
- * another class loader. This is Java's naming rule.
- * </p>
- *
- * @since 3.24
- */
- public static class Simple extends ClassLoader {
- /**
- * Constructs a class loader.
- */
- public Simple() {}
-
- /**
- * Constructs a class loader.
- * @param parent the parent class loader.
- */
- public Simple(ClassLoader parent) {
- super(parent);
- }
-
- /**
- * Invokes the protected {@code defineClass()} in {@code ClassLoader}.
- * It converts the given {@link CtClass} object into a {@code java.lang.Class} object.
- */
- public Class<?> invokeDefineClass(CtClass cc) throws IOException, CannotCompileException {
- byte[] code = cc.toBytecode();
- return defineClass(cc.getName(), code, 0, code.length);
- }
- }
-
- private HashMap<String,ClassLoader> notDefinedHere; // must be atomic.
- private Vector<String> notDefinedPackages; // must be atomic.
- private 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(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, ClassPool cp) {
- super(parent);
- init(cp);
- }
-
- private void init(ClassPool cp) {
- notDefinedHere = new HashMap<String,ClassLoader>();
- notDefinedPackages = new Vector<String>();
- 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(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 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.
- * <br> {@code args[0]} is the class name to be loaded.
- * <br> {@code args[1..n]} are parameters passed
- * to the target {@code main()}.
- *
- * @see javassist.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.
- *
- * <br> {@code args[0]} is the class name to be loaded.
- * <br> {@code args[1..n]} are parameters passed
- * to the target {@code main()}.
- */
- public void run(String[] args) throws Throwable {
- if (args.length >= 1)
- run(args[0], Arrays.copyOfRange(args, 1, args.length));
- }
-
- /**
- * 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 (InvocationTargetException e) {
- throw e.getTargetException();
- }
- }
-
- /**
- * Requests the class loader to load a class.
- */
- @Override
- 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.
- */
- @Override
- 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 = 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 (isDefinedPackage(pname))
- 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);
- return defineClass(name, classfile, 0, classfile.length, domain);
- }
-
- private boolean isDefinedPackage(String name) {
- if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9)
- return getDefinedPackage(name) == null;
- else
- return getPackage(name) == null;
- }
-
- 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.containsKey(name))
- return true;
-
- for (String pack : notDefinedPackages)
- if (name.startsWith(pack))
- return true;
-
- return false;
- }
-
- protected Class<?> delegateToParent(String classname)
- throws ClassNotFoundException
- {
- ClassLoader cl = getParent();
- if (cl != null)
- return cl.loadClass(classname);
- return findSystemClass(classname);
- }
- }
|