History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/invoke/anon/AnonymousClassLoader.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/invoke/anon/AnonymousClassLoader.java+290−0
@@ -0,0 +1,290 @@
1/*
2 * Copyright (c) 2008, 2011, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.invoke.anon;
27
28import java.io.IOException;
29import java.lang.reflect.InvocationTargetException;
30import java.lang.reflect.Method;
31import sun.misc.IOUtils;
32
33/**
34 * Anonymous class loader. Will load any valid classfile, producing
35 * a {@link Class} metaobject, without installing that class in the
36 * system dictionary. Therefore, {@link Class#forName(String)} will never
37 * produce a reference to an anonymous class.
38 * <p>
39 * The access permissions of the anonymous class are borrowed from
40 * a <em>host class</em>. The new class behaves as if it were an
41 * inner class of the host class. It can access the host's private
42 * members, if the creator of the class loader has permission to
43 * do so (or to create accessible reflective objects).
44 * <p>
45 * When the anonymous class is loaded, elements of its constant pool
46 * can be patched to new values. This provides a hook to pre-resolve
47 * named classes in the constant pool to other classes, including
48 * anonymous ones. Also, string constants can be pre-resolved to
49 * any reference. (The verifier treats non-string, non-class reference
50 * constants as plain objects.)
51 * <p>
52 * Why include the patching function? It makes some use cases much easier.
53 * Second, the constant pool needed some internal patching anyway,
54 * to anonymize the loaded class itself. Finally, if you are going
55 * to use this seriously, you'll want to build anonymous classes
56 * on top of pre-existing anonymous classes, and that requires patching.
57 *
58 * <p>%%% TO-DO:
59 * <ul>
60 * <li>needs better documentation</li>
61 * <li>needs more security work (for safe delegation)</li>
62 * <li>needs a clearer story about error processing</li>
63 * <li>patch member references also (use ';' as delimiter char)</li>
64 * <li>patch method references to (conforming) method handles</li>
65 * </ul>
66 *
67 * @author jrose
68 * @author Remi Forax
69 * @see <a href="http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm">
70 * http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm</a>
71 */
72
73public class AnonymousClassLoader {
74 final Class<?> hostClass;
75
76 // Note: Do not refactor the calls to checkHostClass unless you
77 // also adjust this constant:
78 private static int CHC_CALLERS = 3;
79
80 public AnonymousClassLoader() {
81 this.hostClass = checkHostClass(null);
82 }
83 public AnonymousClassLoader(Class<?> hostClass) {
84 this.hostClass = checkHostClass(hostClass);
85 }
86
87 private static Class<?> getTopLevelClass(Class<?> clazz) {
88 for(Class<?> outer = clazz.getDeclaringClass(); outer != null;
89 outer = outer.getDeclaringClass()) {
90 clazz = outer;
91 }
92 return clazz;
93 }
94
95 private static Class<?> checkHostClass(Class<?> hostClass) {
96 // called only from the constructor
97 // does a context-sensitive check on caller class
98 // CC[0..3] = {Reflection, this.checkHostClass, this.<init>, caller}
99 Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
100
101 if (caller == null) {
102 // called from the JVM directly
103 if (hostClass == null)
104 return AnonymousClassLoader.class; // anything central will do
105 return hostClass;
106 }
107
108 if (hostClass == null)
109 hostClass = caller; // default value is caller itself
110
111 // anonymous class will access hostClass on behalf of caller
112 Class<?> callee = hostClass;
113
114 if (caller == callee)
115 // caller can always nominate itself to grant caller's own access rights
116 return hostClass;
117
118 // normalize caller and callee to their top-level classes:
119 caller = getTopLevelClass(caller);
120 callee = getTopLevelClass(callee);
121 if (caller == callee)
122 return caller;
123
124 ClassLoader callerCL = caller.getClassLoader();
125 if (callerCL == null) {
126 // caller is trusted code, so accept the proposed hostClass
127 return hostClass;
128 }
129
130 // %%% should do something with doPrivileged, because trusted
131 // code should have a way to execute on behalf of
132 // partially-trusted clients
133
134 // Does the caller have the right to access the private
135 // members of the callee? If not, raise an error.
136 final int ACC_PRIVATE = 2;
137 try {
138 sun.reflect.Reflection.ensureMemberAccess(caller, callee, null, ACC_PRIVATE);
139 } catch (IllegalAccessException ee) {
140 throw new IllegalArgumentException(ee);
141 }
142
143 return hostClass;
144 }
145
146 public Class<?> loadClass(byte[] classFile) {
147 if (defineAnonymousClass == null) {
148 // no JVM support; try to fake an approximation
149 try {
150 return fakeLoadClass(new ConstantPoolParser(classFile).createPatch());
151 } catch (InvalidConstantPoolFormatException ee) {
152 throw new IllegalArgumentException(ee);
153 }
154 }
155 return loadClass(classFile, null);
156 }
157
158 public Class<?> loadClass(ConstantPoolPatch classPatch) {
159 if (defineAnonymousClass == null) {
160 // no JVM support; try to fake an approximation
161 return fakeLoadClass(classPatch);
162 }
163 Object[] patches = classPatch.patchArray;
164 // Convert class names (this late in the game)
165 // to use slash '/' instead of dot '.'.
166 // Java likes dots, but the JVM likes slashes.
167 for (int i = 0; i < patches.length; i++) {
168 Object value = patches[i];
169 if (value != null) {
170 byte tag = classPatch.getTag(i);
171 switch (tag) {
172 case ConstantPoolVisitor.CONSTANT_Class:
173 if (value instanceof String) {
174 if (patches == classPatch.patchArray)
175 patches = patches.clone();
176 patches[i] = ((String)value).replace('.', '/');
177 }
178 break;
179 case ConstantPoolVisitor.CONSTANT_Fieldref:
180 case ConstantPoolVisitor.CONSTANT_Methodref:
181 case ConstantPoolVisitor.CONSTANT_InterfaceMethodref:
182 case ConstantPoolVisitor.CONSTANT_NameAndType:
183 // When/if the JVM supports these patches,
184 // we'll probably need to reformat them also.
185 // Meanwhile, let the class loader create the error.
186 break;
187 }
188 }
189 }
190 return loadClass(classPatch.outer.classFile, classPatch.patchArray);
191 }
192
193 private Class<?> loadClass(byte[] classFile, Object[] patchArray) {
194 try {
195 return (Class<?>)
196 defineAnonymousClass.invoke(unsafe,
197 hostClass, classFile, patchArray);
198 } catch (Exception ex) {
199 throwReflectedException(ex);
200 throw new RuntimeException("error loading into "+hostClass, ex);
201 }
202 }
203
204 private static void throwReflectedException(Exception ex) {
205 if (ex instanceof InvocationTargetException) {
206 Throwable tex = ((InvocationTargetException)ex).getTargetException();
207 if (tex instanceof Error)
208 throw (Error) tex;
209 ex = (Exception) tex;
210 }
211 if (ex instanceof RuntimeException) {
212 throw (RuntimeException) ex;
213 }
214 }
215
216 private Class<?> fakeLoadClass(ConstantPoolPatch classPatch) {
217 // Implementation:
218 // 1. Make up a new name nobody has used yet.
219 // 2. Inspect the tail-header of the class to find the this_class index.
220 // 3. Patch the CONSTANT_Class for this_class to the new name.
221 // 4. Add other CP entries required by (e.g.) string patches.
222 // 5. Flatten Class constants down to their names, making sure that
223 // the host class loader can pick them up again accurately.
224 // 6. Generate the edited class file bytes.
225 //
226 // Potential limitations:
227 // * The class won't be truly anonymous, and may interfere with others.
228 // * Flattened class constants might not work, because of loader issues.
229 // * Pseudo-string constants will not flatten down to real strings.
230 // * Method handles will (of course) fail to flatten to linkage strings.
231 if (true) throw new UnsupportedOperationException("NYI");
232 Object[] cpArray;
233 try {
234 cpArray = classPatch.getOriginalCP();
235 } catch (InvalidConstantPoolFormatException ex) {
236 throw new RuntimeException(ex);
237 }
238 int thisClassIndex = classPatch.getParser().getThisClassIndex();
239 String thisClassName = (String) cpArray[thisClassIndex];
240 synchronized (AnonymousClassLoader.class) {
241 thisClassName = thisClassName+"\\|"+(++fakeNameCounter);
242 }
243 classPatch.putUTF8(thisClassIndex, thisClassName);
244 byte[] classFile = null;
245 return unsafe.defineClass(null, classFile, 0, classFile.length,
246 hostClass.getClassLoader(),
247 hostClass.getProtectionDomain());
248 }
249 private static int fakeNameCounter = 99999;
250
251 // ignore two warnings on this line:
252 static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
253 // preceding line requires that this class be on the boot class path
254
255 static private final Method defineAnonymousClass;
256 static {
257 Method dac = null;
258 Class<? extends sun.misc.Unsafe> unsafeClass = unsafe.getClass();
259 try {
260 dac = unsafeClass.getMethod("defineAnonymousClass",
261 Class.class,
262 byte[].class,
263 Object[].class);
264 } catch (Exception ee) {
265 dac = null;
266 }
267 defineAnonymousClass = dac;
268 }
269
270 private static void noJVMSupport() {
271 throw new UnsupportedOperationException("no JVM support for anonymous classes");
272 }
273
274
275 private static native Class<?> loadClassInternal(Class<?> hostClass,
276 byte[] classFile,
277 Object[] patchArray);
278
279 public static byte[] readClassFile(Class<?> templateClass) throws IOException {
280 String templateName = templateClass.getName();
281 int lastDot = templateName.lastIndexOf('.');
282 java.net.URL url = templateClass.getResource(templateName.substring(lastDot+1)+".class");
283 java.net.URLConnection connection = url.openConnection();
284 int contentLength = connection.getContentLength();
285 if (contentLength < 0)
286 throw new IOException("invalid content length "+contentLength);
287
288 return IOUtils.readFully(connection.getInputStream(), contentLength, true);
289 }
290}