• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later,
9  * or the Apache License Version 2.0.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  */
16 
17 package javassist;
18 
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.lang.reflect.InvocationTargetException;
22 import java.security.ProtectionDomain;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.Vector;
26 
27 import javassist.bytecode.ClassFile;
28 
29 /**
30  * The class loader for Javassist.
31  *
32  * <p>This is a sample class loader using <code>ClassPool</code>.
33  * Unlike a regular class loader, this class loader obtains bytecode
34  * from a <code>ClassPool</code>.
35  *
36  * <p>Note that Javassist can be used without this class loader; programmers
37  * can define their own versions of class loader.  They can run
38  * a program even without any user-defined class loader if that program
39  * is statically translated with Javassist.
40  * This class loader is just provided as a utility class.
41  *
42  * <p>Suppose that an instance of <code>MyTranslator</code> implementing
43  * the interface <code>Translator</code> is responsible for modifying
44  * class files.
45  * The startup program of an application using <code>MyTranslator</code>
46  * should be something like this:
47  *
48  * <pre>
49  * import javassist.*;
50  *
51  * public class Main {
52  *   public static void main(String[] args) throws Throwable {
53  *     MyTranslator myTrans = new MyTranslator();
54  *     ClassPool cp = ClassPool.getDefault();
55  *     Loader cl = new Loader(cp);
56  *     cl.addTranslator(cp, myTrans);
57  *     cl.run("MyApp", args);
58  *   }
59  * }
60  * </pre>
61  *
62  * <p>Class <code>MyApp</code> is the main program of the application.
63  *
64  * <p>This program should be executed as follows:
65  *
66  * <pre>
67  * % java Main <i>arg1</i> <i>arg2</i>...
68  * </pre>
69  *
70  * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code>
71  * object before the JVM loads it.
72  * Then it calls <code>main()</code> in <code>MyApp</code> with arguments
73  * <i>arg1</i>, <i>arg2</i>, ...
74  *
75  * <p>This program execution is equivalent to:
76  *
77  * <pre>
78  * % java MyApp <i>arg1</i> <i>arg2</i>...
79  * </pre>
80  *
81  * <p>except that classes are translated by <code>MyTranslator</code>
82  * at load time.
83  *
84  * <p>If only a particular class must be modified when it is loaded,
85  * the startup program can be simpler; <code>MyTranslator</code> is
86  * unnecessary.  For example, if only a class <code>test.Rectangle</code>
87  * is modified, the <code>main()</code> method above will be the following:
88  *
89  * <pre>
90  * ClassPool cp = ClassPool.getDefault();
91  * Loader cl = new Loader(cp);
92  * CtClass ct = cp.get("test.Rectangle");
93  * ct.setSuperclass(cp.get("test.Point"));
94  * cl.run("MyApp", args);</pre>
95  *
96  * <p>This program changes the super class of the <code>test.Rectangle</code>
97  * class.
98  *
99  * <p><b>Note 1:</b>
100  *
101  * <p>This class loader does not allow the users to intercept the loading
102  * of <code>java.*</code> and <code>javax.*</code> classes (and
103  * <code>sun.*</code>, <code>org.xml.*</code>, ...) unless
104  * <code>Loader.doDelegation</code> is <code>false</code>.  This is because
105  * the JVM prohibits a user class loader from loading a system class.
106  * Also see Note 2.
107  * If this behavior is not appropriate, a subclass of <code>Loader</code>
108  * must be defined and <code>loadClassByDelegation()</code> must be overridden.
109  *
110  * <p><b>Note 2:</b>
111  *
112  * <p>If classes are loaded with different class loaders, they belong to
113  * separate name spaces.  If class <code>C</code> is loaded by a class
114  * loader <code>CL</code>, all classes that the class <code>C</code>
115  * refers to are also loaded by <code>CL</code>.  However, if <code>CL</code>
116  * delegates the loading of the class <code>C</code> to <code>CL'</code>,
117  * then those classes that the class <code>C</code> refers to
118  * are loaded by a parent class loader <code>CL'</code>
119  * instead of <code>CL</code>.
120  *
121  * <p>If an object of class <code>C</code> is assigned
122  * to a variable of class <code>C</code> belonging to a different name
123  * space, then a <code>ClassCastException</code> is thrown.
124  *
125  * <p>Because of the fact above, this loader delegates only the loading of
126  * <code>javassist.Loader</code>
127  * and classes included in package <code>java.*</code> and
128  * <code>javax.*</code> to the parent class
129  * loader.  Other classes are directly loaded by this loader.
130  *
131  * <p>For example, suppose that <code>java.lang.String</code> would be loaded
132  * by this loader while <code>java.io.File</code> is loaded by the parent
133  * class loader.  If the constructor of <code>java.io.File</code> is called
134  * with an instance of <code>java.lang.String</code>, then it may throw
135  * an exception since it accepts an instance of only the
136  * <code>java.lang.String</code> loaded by the parent class loader.
137  *
138  * @see javassist.ClassPool
139  * @see javassist.Translator
140  */
141 public class Loader extends ClassLoader {
142 
143     /**
144      * A simpler class loader.
145      * This is a class loader that exposes the protected {@code defineClass()} method
146      * declared in {@code java.lang.ClassLoader}.  It provides a method similar to
147      * {@code CtClass#toClass()}.
148      *
149      * <p>When loading a class, this class loader delegates the work to the
150      * parent class loader unless the loaded classes are explicitly given
151      * by {@link #invokeDefineClass(CtClass)}.
152      * Note that a class {@code Foo} loaded by this class loader is
153      * different from the class with the same name {@code Foo} but loaded by
154      * another class loader.  This is Java's naming rule.
155      * </p>
156      *
157      * @since 3.24
158      */
159     public static class Simple extends ClassLoader {
160         /**
161          * Constructs a class loader.
162          */
Simple()163         public Simple() {}
164 
165         /**
166          * Constructs a class loader.
167          * @param parent    the parent class loader.
168          */
Simple(ClassLoader parent)169         public Simple(ClassLoader parent) {
170             super(parent);
171         }
172 
173         /**
174          * Invokes the protected {@code defineClass()} in {@code ClassLoader}.
175          * It converts the given {@link CtClass} object into a {@code java.lang.Class} object.
176          */
invokeDefineClass(CtClass cc)177         public Class<?> invokeDefineClass(CtClass cc) throws IOException, CannotCompileException {
178             byte[] code = cc.toBytecode();
179             return defineClass(cc.getName(), code, 0, code.length);
180         }
181     }
182 
183     private HashMap<String,ClassLoader> notDefinedHere; // must be atomic.
184     private Vector<String> notDefinedPackages; // must be atomic.
185     private ClassPool source;
186     private Translator translator;
187     private ProtectionDomain domain;
188 
189     /**
190      * Specifies the algorithm of class loading.
191      *
192      * <p>This class loader uses the parent class loader for
193      * <code>java.*</code> and <code>javax.*</code> classes.
194      * If this variable <code>doDelegation</code>
195      * is <code>false</code>, this class loader does not delegate those
196      * classes to the parent class loader.
197      *
198      * <p>The default value is <code>true</code>.
199      */
200     public boolean doDelegation = true;
201 
202     /**
203      * Creates a new class loader.
204      */
Loader()205     public Loader() {
206         this(null);
207     }
208 
209     /**
210      * Creates a new class loader.
211      *
212      * @param cp        the source of class files.
213      */
Loader(ClassPool cp)214     public Loader(ClassPool cp) {
215         init(cp);
216     }
217 
218     /**
219      * Creates a new class loader
220      * using the specified parent class loader for delegation.
221      *
222      * @param parent    the parent class loader.
223      * @param cp        the source of class files.
224      */
Loader(ClassLoader parent, ClassPool cp)225     public Loader(ClassLoader parent, ClassPool cp) {
226         super(parent);
227         init(cp);
228     }
229 
init(ClassPool cp)230     private void init(ClassPool cp) {
231         notDefinedHere = new HashMap<String,ClassLoader>();
232         notDefinedPackages = new Vector<String>();
233         source = cp;
234         translator = null;
235         domain = null;
236         delegateLoadingOf("javassist.Loader");
237     }
238 
239     /**
240      * Records a class so that the loading of that class is delegated
241      * to the parent class loader.
242      *
243      * <p>If the given class name ends with <code>.</code> (dot), then
244      * that name is interpreted as a package name.  All the classes
245      * in that package and the sub packages are delegated.
246      */
delegateLoadingOf(String classname)247     public void delegateLoadingOf(String classname) {
248         if (classname.endsWith("."))
249             notDefinedPackages.addElement(classname);
250         else
251             notDefinedHere.put(classname, this);
252     }
253 
254     /**
255      * Sets the protection domain for the classes handled by this class
256      * loader.  Without registering an appropriate protection domain,
257      * the program loaded by this loader will not work with a security
258      * manager or a signed jar file.
259      */
setDomain(ProtectionDomain d)260     public void setDomain(ProtectionDomain d) {
261         domain = d;
262     }
263 
264     /**
265      * Sets the soruce <code>ClassPool</code>.
266      */
setClassPool(ClassPool cp)267     public void setClassPool(ClassPool cp) {
268         source = cp;
269     }
270 
271     /**
272      * Adds a translator, which is called whenever a class is loaded.
273      *
274      * @param cp        the <code>ClassPool</code> object for obtaining
275      *                  a class file.
276      * @param t         a translator.
277      * @throws NotFoundException        if <code>t.start()</code> throws an exception.
278      * @throws CannotCompileException   if <code>t.start()</code> throws an exception.
279      */
addTranslator(ClassPool cp, Translator t)280     public void addTranslator(ClassPool cp, Translator t)
281         throws NotFoundException, CannotCompileException {
282         source = cp;
283         translator = t;
284         t.start(cp);
285     }
286 
287     /**
288      * Loads a class with an instance of <code>Loader</code>
289      * and calls <code>main()</code> of that class.
290      *
291      * <p>This method calls <code>run()</code>.
292      *
293      * @param args              command line parameters.
294      * <br>&nbsp;&nbsp;{@code args[0]} is the class name to be loaded.
295      * <br>&nbsp;&nbsp;{@code args[1..n]} are parameters passed
296      *                      to the target {@code main()}.
297      *
298      * @see javassist.Loader#run(String[])
299      */
main(String[] args)300     public static void main(String[] args) throws Throwable {
301         Loader cl = new Loader();
302         cl.run(args);
303     }
304 
305     /**
306      * Loads a class and calls <code>main()</code> in that class.
307      *
308      * @param args              command line parameters.
309      *
310      * <br>&nbsp;&nbsp;{@code args[0]} is the class name to be loaded.
311      * <br>&nbsp;&nbsp;{@code args[1..n]} are parameters passed
312      *                      to the target {@code main()}.
313      */
run(String[] args)314     public void run(String[] args) throws Throwable {
315         if (args.length >= 1)
316             run(args[0], Arrays.copyOfRange(args, 1, args.length));
317     }
318 
319     /**
320      * Loads a class and calls <code>main()</code> in that class.
321      *
322      * @param classname         the loaded class.
323      * @param args              parameters passed to <code>main()</code>.
324      */
run(String classname, String[] args)325     public void run(String classname, String[] args) throws Throwable {
326         Class<?> c = loadClass(classname);
327         try {
328             c.getDeclaredMethod("main", new Class<?>[] { String[].class }).invoke(
329                 null,
330                 new Object[] { args });
331         }
332         catch (InvocationTargetException e) {
333             throw e.getTargetException();
334         }
335     }
336 
337     /**
338      * Requests the class loader to load a class.
339      */
340     @Override
loadClass(String name, boolean resolve)341     protected Class<?> loadClass(String name, boolean resolve)
342         throws ClassFormatError, ClassNotFoundException {
343         name = name.intern();
344         synchronized (name) {
345             Class<?> c = findLoadedClass(name);
346             if (c == null)
347                 c = loadClassByDelegation(name);
348 
349             if (c == null)
350                 c = findClass(name);
351 
352             if (c == null)
353                 c = delegateToParent(name);
354 
355             if (resolve)
356                 resolveClass(c);
357 
358             return c;
359         }
360     }
361 
362     /**
363      * Finds the specified class using <code>ClassPath</code>.
364      * If the source throws an exception, this returns null.
365      *
366      * <p>This method can be overridden by a subclass of
367      * <code>Loader</code>.  Note that the overridden method must not throw
368      * an exception when it just fails to find a class file.
369      *
370      * @return      null if the specified class could not be found.
371      * @throws ClassNotFoundException   if an exception is thrown while
372      *                                  obtaining a class file.
373      */
374     @Override
findClass(String name)375     protected Class<?> findClass(String name) throws ClassNotFoundException {
376         byte[] classfile;
377         try {
378             if (source != null) {
379                 if (translator != null)
380                     translator.onLoad(source, name);
381 
382                 try {
383                     classfile = source.get(name).toBytecode();
384                 }
385                 catch (NotFoundException e) {
386                     return null;
387                 }
388             }
389             else {
390                 String jarname = "/" + name.replace('.', '/') + ".class";
391                 InputStream in = this.getClass().getResourceAsStream(jarname);
392                 if (in == null)
393                     return null;
394 
395                 classfile = ClassPoolTail.readStream(in);
396             }
397         }
398         catch (Exception e) {
399             throw new ClassNotFoundException(
400                 "caught an exception while obtaining a class file for "
401                 + name, e);
402         }
403 
404         int i = name.lastIndexOf('.');
405         if (i != -1) {
406             String pname = name.substring(0, i);
407             if (isDefinedPackage(pname))
408                 try {
409                     definePackage(
410                         pname, null, null, null, null, null, null, null);
411                 }
412                 catch (IllegalArgumentException e) {
413                     // ignore.  maybe the package object for the same
414                     // name has been created just right away.
415                 }
416         }
417 
418         if (domain == null)
419             return defineClass(name, classfile, 0, classfile.length);
420         return defineClass(name, classfile, 0, classfile.length, domain);
421     }
422 
isDefinedPackage(String name)423     private boolean isDefinedPackage(String name) {
424         if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9)
425             return getDefinedPackage(name) == null;
426         else
427             return getPackage(name) == null;
428     }
429 
loadClassByDelegation(String name)430     protected Class<?> loadClassByDelegation(String name)
431         throws ClassNotFoundException
432     {
433         /* The swing components must be loaded by a system
434          * class loader.
435          * javax.swing.UIManager loads a (concrete) subclass
436          * of LookAndFeel by a system class loader and cast
437          * an instance of the class to LookAndFeel for
438          * (maybe) a security reason.  To avoid failure of
439          * type conversion, LookAndFeel must not be loaded
440          * by this class loader.
441          */
442 
443         Class<?> c = null;
444         if (doDelegation)
445             if (name.startsWith("java.")
446                 || name.startsWith("javax.")
447                 || name.startsWith("sun.")
448                 || name.startsWith("com.sun.")
449                 || name.startsWith("org.w3c.")
450                 || name.startsWith("org.xml.")
451                 || notDelegated(name))
452                 c = delegateToParent(name);
453 
454         return c;
455     }
456 
notDelegated(String name)457     private boolean notDelegated(String name) {
458         if (notDefinedHere.containsKey(name))
459             return true;
460 
461         for (String pack : notDefinedPackages)
462             if (name.startsWith(pack))
463                 return true;
464 
465         return false;
466     }
467 
delegateToParent(String classname)468     protected Class<?> delegateToParent(String classname)
469         throws ClassNotFoundException
470     {
471         ClassLoader cl = getParent();
472         if (cl != null)
473             return cl.loadClass(classname);
474         return findSystemClass(classname);
475     }
476 }
477