1 /*
2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.io.BufferedInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.Vector;
30 import jdk.internal.org.objectweb.asm.*;
31 // Compile with -XDignore.symbol.file=true
32 
33 public class BogoLoader extends ClassLoader {
34 
35     static interface VisitorMaker {
make(ClassVisitor visitor)36     ClassVisitor make(ClassVisitor visitor);
37     }
38 
39 
40     /**
41      * Use this property to verify that the desired classloading is happening.
42      */
43     private final boolean verbose = Boolean.getBoolean("bogoloader.verbose");
44     /**
45      * Use this property to disable replacement for testing purposes.
46      */
47     private final boolean noReplace = Boolean.getBoolean("bogoloader.noreplace");
48 
49     /**
50      * Set of class names that should be loaded with this loader.
51      * Others are loaded with the system class loader, except for those
52      * that are transformed.
53      */
54     private Set<String> nonSystem;
55 
56     /**
57      * Map from class names to a bytecode transformer factory.
58      */
59     private Map<String, VisitorMaker> replaced;
60 
61     /**
62      * Keep track (not terribly efficiently) of which classes have already
63      * been loaded by this class loader.
64      */
65     private final Vector<String> history = new Vector<String>();
66 
useSystemLoader(String name)67     private boolean useSystemLoader(String name) {
68         return ! nonSystem.contains(name) && ! replaced.containsKey(name);
69     }
70 
BogoLoader(Set<String> non_system, Map<String, VisitorMaker> replaced)71     public BogoLoader(Set<String> non_system, Map<String, VisitorMaker> replaced) {
72         super(Thread.currentThread().getContextClassLoader());
73         this.nonSystem = non_system;
74         this.replaced = replaced;
75     }
76 
readResource(String className)77     private byte[] readResource(String className) throws IOException {
78         return readResource(className, "class");
79     }
80 
readResource(String className, String suffix)81     private byte[] readResource(String className, String suffix) throws IOException {
82         // Note to the unwary -- "/" works on Windows, leave it alone.
83         String fileName = className.replace('.', '/') + "." + suffix;
84         InputStream origStream = getResourceAsStream(fileName);
85         if (origStream == null) {
86             throw new IOException("Resource not found : " + fileName);
87         }
88         BufferedInputStream stream = new java.io.BufferedInputStream(origStream);
89         byte[] data = new byte[stream.available()];
90         int how_many = stream.read(data);
91         // Really ought to deal with the corner cases of stream.available()
92         return data;
93     }
94 
getClass(String name)95     protected byte[] getClass(String name) throws ClassNotFoundException,
96     IOException {
97         return readResource(name, "class");
98     }
99 
100     /**
101      * Loads the named class from the system class loader unless
102      * the name appears in either replaced or nonSystem.
103      * nonSystem classes are loaded into this classloader,
104      * and replaced classes get their content from the specified array
105      * of bytes (and are also loaded into this classloader).
106      */
loadClass(String name, boolean resolve)107     protected Class<?> loadClass(String name, boolean resolve)
108             throws ClassNotFoundException {
109         Class<?> clazz;
110 
111         if (history.contains(name)) {
112             Class<?> c = this.findLoadedClass(name);
113             return c;
114         }
115         if (useSystemLoader(name)) {
116             clazz = findSystemClass(name);
117             if (verbose) System.err.println("Loading system class " + name);
118         } else {
119             history.add(name);
120             try {
121                 if (verbose) {
122                     System.err.println("Loading classloader class " + name);
123                 }
124                 byte[] classData = getClass(name);;
125                 boolean expanded = false;
126                 if (!noReplace && replaced.containsKey(name)) {
127                     if (verbose) {
128                         System.err.println("Replacing class " + name);
129                     }
130                     ClassReader cr = new ClassReader(classData);
131                     ClassWriter cw = new ClassWriter(0);
132                     VisitorMaker vm = replaced.get(name);
133                     cr.accept(vm.make(cw), 0);
134                     classData = cw.toByteArray();
135                 }
136                 clazz = defineClass(name, classData, 0, classData.length);
137             } catch (java.io.EOFException ioe) {
138                 throw new ClassNotFoundException(
139                         "IO Exception in reading class : " + name + " ", ioe);
140             } catch (ClassFormatError ioe) {
141                 throw new ClassNotFoundException(
142                         "ClassFormatError in reading class file: ", ioe);
143             } catch (IOException ioe) {
144                 throw new ClassNotFoundException(
145                         "IO Exception in reading class file: ", ioe);
146             }
147         }
148         if (clazz == null) {
149             throw new ClassNotFoundException(name);
150         }
151         if (resolve) {
152             resolveClass(clazz);
153         }
154         return clazz;
155     }
156 }
157