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+
26+package sun.invoke.anon;
27+
28+import java.io.IOException;
29+import java.lang.reflect.InvocationTargetException;
30+import java.lang.reflect.Method;
31+import 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+
73+public 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+}