1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/emul/compact/src/main/java/sun/invoke/anon/AnonymousClassLoader.java Sat Aug 09 11:11:13 2014 +0200
1.3 @@ -0,0 +1,230 @@
1.4 +/*
1.5 + * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package sun.invoke.anon;
1.30 +
1.31 +import java.io.IOException;
1.32 +import java.lang.reflect.InvocationTargetException;
1.33 +import java.lang.reflect.Method;
1.34 +import sun.misc.IOUtils;
1.35 +
1.36 +/**
1.37 + * Anonymous class loader. Will load any valid classfile, producing
1.38 + * a {@link Class} metaobject, without installing that class in the
1.39 + * system dictionary. Therefore, {@link Class#forName(String)} will never
1.40 + * produce a reference to an anonymous class.
1.41 + * <p>
1.42 + * The access permissions of the anonymous class are borrowed from
1.43 + * a <em>host class</em>. The new class behaves as if it were an
1.44 + * inner class of the host class. It can access the host's private
1.45 + * members, if the creator of the class loader has permission to
1.46 + * do so (or to create accessible reflective objects).
1.47 + * <p>
1.48 + * When the anonymous class is loaded, elements of its constant pool
1.49 + * can be patched to new values. This provides a hook to pre-resolve
1.50 + * named classes in the constant pool to other classes, including
1.51 + * anonymous ones. Also, string constants can be pre-resolved to
1.52 + * any reference. (The verifier treats non-string, non-class reference
1.53 + * constants as plain objects.)
1.54 + * <p>
1.55 + * Why include the patching function? It makes some use cases much easier.
1.56 + * Second, the constant pool needed some internal patching anyway,
1.57 + * to anonymize the loaded class itself. Finally, if you are going
1.58 + * to use this seriously, you'll want to build anonymous classes
1.59 + * on top of pre-existing anonymous classes, and that requires patching.
1.60 + *
1.61 + * <p>%%% TO-DO:
1.62 + * <ul>
1.63 + * <li>needs better documentation</li>
1.64 + * <li>needs more security work (for safe delegation)</li>
1.65 + * <li>needs a clearer story about error processing</li>
1.66 + * <li>patch member references also (use ';' as delimiter char)</li>
1.67 + * <li>patch method references to (conforming) method handles</li>
1.68 + * </ul>
1.69 + *
1.70 + * @author jrose
1.71 + * @author Remi Forax
1.72 + * @see <a href="http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm">
1.73 + * http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm</a>
1.74 + */
1.75 +
1.76 +public class AnonymousClassLoader {
1.77 + final Class<?> hostClass;
1.78 +
1.79 + // Privileged constructor.
1.80 + private AnonymousClassLoader(Class<?> hostClass) {
1.81 + this.hostClass = hostClass;
1.82 + }
1.83 +
1.84 + public static AnonymousClassLoader make(sun.misc.Unsafe unsafe, Class<?> hostClass) {
1.85 + if (unsafe == null) throw new NullPointerException();
1.86 + return new AnonymousClassLoader(hostClass);
1.87 + }
1.88 +
1.89 + public Class<?> loadClass(byte[] classFile) {
1.90 + if (defineAnonymousClass == null) {
1.91 + // no JVM support; try to fake an approximation
1.92 + try {
1.93 + return fakeLoadClass(new ConstantPoolParser(classFile).createPatch());
1.94 + } catch (InvalidConstantPoolFormatException ee) {
1.95 + throw new IllegalArgumentException(ee);
1.96 + }
1.97 + }
1.98 + return loadClass(classFile, null);
1.99 + }
1.100 +
1.101 + public Class<?> loadClass(ConstantPoolPatch classPatch) {
1.102 + if (defineAnonymousClass == null) {
1.103 + // no JVM support; try to fake an approximation
1.104 + return fakeLoadClass(classPatch);
1.105 + }
1.106 + Object[] patches = classPatch.patchArray;
1.107 + // Convert class names (this late in the game)
1.108 + // to use slash '/' instead of dot '.'.
1.109 + // Java likes dots, but the JVM likes slashes.
1.110 + for (int i = 0; i < patches.length; i++) {
1.111 + Object value = patches[i];
1.112 + if (value != null) {
1.113 + byte tag = classPatch.getTag(i);
1.114 + switch (tag) {
1.115 + case ConstantPoolVisitor.CONSTANT_Class:
1.116 + if (value instanceof String) {
1.117 + if (patches == classPatch.patchArray)
1.118 + patches = patches.clone();
1.119 + patches[i] = ((String)value).replace('.', '/');
1.120 + }
1.121 + break;
1.122 + case ConstantPoolVisitor.CONSTANT_Fieldref:
1.123 + case ConstantPoolVisitor.CONSTANT_Methodref:
1.124 + case ConstantPoolVisitor.CONSTANT_InterfaceMethodref:
1.125 + case ConstantPoolVisitor.CONSTANT_NameAndType:
1.126 + // When/if the JVM supports these patches,
1.127 + // we'll probably need to reformat them also.
1.128 + // Meanwhile, let the class loader create the error.
1.129 + break;
1.130 + }
1.131 + }
1.132 + }
1.133 + return loadClass(classPatch.outer.classFile, classPatch.patchArray);
1.134 + }
1.135 +
1.136 + private Class<?> loadClass(byte[] classFile, Object[] patchArray) {
1.137 + try {
1.138 + return (Class<?>)
1.139 + defineAnonymousClass.invoke(unsafe,
1.140 + hostClass, classFile, patchArray);
1.141 + } catch (Exception ex) {
1.142 + throwReflectedException(ex);
1.143 + throw new RuntimeException("error loading into "+hostClass, ex);
1.144 + }
1.145 + }
1.146 +
1.147 + private static void throwReflectedException(Exception ex) {
1.148 + if (ex instanceof InvocationTargetException) {
1.149 + Throwable tex = ((InvocationTargetException)ex).getTargetException();
1.150 + if (tex instanceof Error)
1.151 + throw (Error) tex;
1.152 + ex = (Exception) tex;
1.153 + }
1.154 + if (ex instanceof RuntimeException) {
1.155 + throw (RuntimeException) ex;
1.156 + }
1.157 + }
1.158 +
1.159 + private Class<?> fakeLoadClass(ConstantPoolPatch classPatch) {
1.160 + // Implementation:
1.161 + // 1. Make up a new name nobody has used yet.
1.162 + // 2. Inspect the tail-header of the class to find the this_class index.
1.163 + // 3. Patch the CONSTANT_Class for this_class to the new name.
1.164 + // 4. Add other CP entries required by (e.g.) string patches.
1.165 + // 5. Flatten Class constants down to their names, making sure that
1.166 + // the host class loader can pick them up again accurately.
1.167 + // 6. Generate the edited class file bytes.
1.168 + //
1.169 + // Potential limitations:
1.170 + // * The class won't be truly anonymous, and may interfere with others.
1.171 + // * Flattened class constants might not work, because of loader issues.
1.172 + // * Pseudo-string constants will not flatten down to real strings.
1.173 + // * Method handles will (of course) fail to flatten to linkage strings.
1.174 + if (true) throw new UnsupportedOperationException("NYI");
1.175 + Object[] cpArray;
1.176 + try {
1.177 + cpArray = classPatch.getOriginalCP();
1.178 + } catch (InvalidConstantPoolFormatException ex) {
1.179 + throw new RuntimeException(ex);
1.180 + }
1.181 + int thisClassIndex = classPatch.getParser().getThisClassIndex();
1.182 + String thisClassName = (String) cpArray[thisClassIndex];
1.183 + synchronized (AnonymousClassLoader.class) {
1.184 + thisClassName = thisClassName+"\\|"+(++fakeNameCounter);
1.185 + }
1.186 + classPatch.putUTF8(thisClassIndex, thisClassName);
1.187 + byte[] classFile = null;
1.188 + return unsafe.defineClass(null, classFile, 0, classFile.length,
1.189 + hostClass.getClassLoader(),
1.190 + hostClass.getProtectionDomain());
1.191 + }
1.192 + private static int fakeNameCounter = 99999;
1.193 +
1.194 + // ignore two warnings on this line:
1.195 + private static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
1.196 + // preceding line requires that this class be on the boot class path
1.197 +
1.198 + static private final Method defineAnonymousClass;
1.199 + static {
1.200 + Method dac = null;
1.201 + Class<? extends sun.misc.Unsafe> unsafeClass = unsafe.getClass();
1.202 + try {
1.203 + dac = unsafeClass.getMethod("defineAnonymousClass",
1.204 + Class.class,
1.205 + byte[].class,
1.206 + Object[].class);
1.207 + } catch (Exception ee) {
1.208 + dac = null;
1.209 + }
1.210 + defineAnonymousClass = dac;
1.211 + }
1.212 +
1.213 + private static void noJVMSupport() {
1.214 + throw new UnsupportedOperationException("no JVM support for anonymous classes");
1.215 + }
1.216 +
1.217 +
1.218 + private static native Class<?> loadClassInternal(Class<?> hostClass,
1.219 + byte[] classFile,
1.220 + Object[] patchArray);
1.221 +
1.222 + public static byte[] readClassFile(Class<?> templateClass) throws IOException {
1.223 + String templateName = templateClass.getName();
1.224 + int lastDot = templateName.lastIndexOf('.');
1.225 + java.net.URL url = templateClass.getResource(templateName.substring(lastDot+1)+".class");
1.226 + java.net.URLConnection connection = url.openConnection();
1.227 + int contentLength = connection.getContentLength();
1.228 + if (contentLength < 0)
1.229 + throw new IOException("invalid content length "+contentLength);
1.230 +
1.231 + return IOUtils.readFully(connection.getInputStream(), contentLength, true);
1.232 + }
1.233 +}