1 /* 2 * Copyright (c) 2008, 2012, 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.EOFException; 29 import java.io.IOException; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 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 // Privileged constructor. 77 private AnonymousClassLoader(Class<?> hostClass) { 78 this.hostClass = hostClass; 79 } 80 81 public static AnonymousClassLoader make(sun.misc.Unsafe unsafe, Class<?> hostClass) { 82 if (unsafe == null) throw new NullPointerException(); 83 return new AnonymousClassLoader(hostClass); 84 } 85 86 public Class<?> loadClass(byte[] classFile) { 87 if (defineAnonymousClass == null) { 88 // no JVM support; try to fake an approximation 89 try { 90 return fakeLoadClass(new ConstantPoolParser(classFile).createPatch()); 91 } catch (InvalidConstantPoolFormatException ee) { 92 throw new IllegalArgumentException(ee); 93 } 94 } 95 return loadClass(classFile, null); 96 } 97 98 public Class<?> loadClass(ConstantPoolPatch classPatch) { 99 if (defineAnonymousClass == null) { 100 // no JVM support; try to fake an approximation 101 return fakeLoadClass(classPatch); 102 } 103 Object[] patches = classPatch.patchArray; 104 // Convert class names (this late in the game) 105 // to use slash '/' instead of dot '.'. 106 // Java likes dots, but the JVM likes slashes. 107 for (int i = 0; i < patches.length; i++) { 108 Object value = patches[i]; 109 if (value != null) { 110 byte tag = classPatch.getTag(i); 111 switch (tag) { 112 case ConstantPoolVisitor.CONSTANT_Class: 113 if (value instanceof String) { 114 if (patches == classPatch.patchArray) 115 patches = patches.clone(); 116 patches[i] = ((String)value).replace('.', '/'); 117 } 118 break; 119 case ConstantPoolVisitor.CONSTANT_Fieldref: 120 case ConstantPoolVisitor.CONSTANT_Methodref: 121 case ConstantPoolVisitor.CONSTANT_InterfaceMethodref: 122 case ConstantPoolVisitor.CONSTANT_NameAndType: 123 // When/if the JVM supports these patches, 124 // we'll probably need to reformat them also. 125 // Meanwhile, let the class loader create the error. 126 break; 127 } 128 } 129 } 130 return loadClass(classPatch.outer.classFile, classPatch.patchArray); 131 } 132 133 private Class<?> loadClass(byte[] classFile, Object[] patchArray) { 134 try { 135 return (Class<?>) 136 defineAnonymousClass.invoke(unsafe, 137 hostClass, classFile, patchArray); 138 } catch (Exception ex) { 139 throwReflectedException(ex); 140 throw new RuntimeException("error loading into "+hostClass, ex); 141 } 142 } 143 144 private static void throwReflectedException(Exception ex) { 145 if (ex instanceof InvocationTargetException) { 146 Throwable tex = ((InvocationTargetException)ex).getTargetException(); 147 if (tex instanceof Error) 148 throw (Error) tex; 149 ex = (Exception) tex; 150 } 151 if (ex instanceof RuntimeException) { 152 throw (RuntimeException) ex; 153 } 154 } 155 156 private Class<?> fakeLoadClass(ConstantPoolPatch classPatch) { 157 // Implementation: 158 // 1. Make up a new name nobody has used yet. 159 // 2. Inspect the tail-header of the class to find the this_class index. 160 // 3. Patch the CONSTANT_Class for this_class to the new name. 161 // 4. Add other CP entries required by (e.g.) string patches. 162 // 5. Flatten Class constants down to their names, making sure that 163 // the host class loader can pick them up again accurately. 164 // 6. Generate the edited class file bytes. 165 // 166 // Potential limitations: 167 // * The class won't be truly anonymous, and may interfere with others. 168 // * Flattened class constants might not work, because of loader issues. 169 // * Pseudo-string constants will not flatten down to real strings. 170 // * Method handles will (of course) fail to flatten to linkage strings. 171 if (true) throw new UnsupportedOperationException("NYI"); 172 Object[] cpArray; 173 try { 174 cpArray = classPatch.getOriginalCP(); 175 } catch (InvalidConstantPoolFormatException ex) { 176 throw new RuntimeException(ex); 177 } 178 int thisClassIndex = classPatch.getParser().getThisClassIndex(); 179 String thisClassName = (String) cpArray[thisClassIndex]; 180 synchronized (AnonymousClassLoader.class) { 181 thisClassName = thisClassName+"\\|"+(++fakeNameCounter); 182 } 183 classPatch.putUTF8(thisClassIndex, thisClassName); 184 byte[] classFile = null; 185 return unsafe.defineClass(null, classFile, 0, classFile.length, 186 hostClass.getClassLoader(), 187 hostClass.getProtectionDomain()); 188 } 189 private static int fakeNameCounter = 99999; 190 191 // ignore two warnings on this line: 192 private static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); 193 // preceding line requires that this class be on the boot class path 194 195 private static final Method defineAnonymousClass; 196 static { 197 Method dac = null; 198 Class<? extends sun.misc.Unsafe> unsafeClass = unsafe.getClass(); 199 try { 200 dac = unsafeClass.getMethod("defineAnonymousClass", 201 Class.class, 202 byte[].class, 203 Object[].class); 204 } catch (Exception ee) { 205 dac = null; 206 } 207 defineAnonymousClass = dac; 208 } 209 210 private static void noJVMSupport() { 211 throw new UnsupportedOperationException("no JVM support for anonymous classes"); 212 } 213 214 215 private static native Class<?> loadClassInternal(Class<?> hostClass, 216 byte[] classFile, 217 Object[] patchArray); 218 219 public static byte[] readClassFile(Class<?> templateClass) throws IOException { 220 String templateName = templateClass.getName(); 221 int lastDot = templateName.lastIndexOf('.'); 222 java.net.URL url = templateClass.getResource(templateName.substring(lastDot+1)+".class"); 223 java.net.URLConnection connection = url.openConnection(); 224 int contentLength = connection.getContentLength(); 225 if (contentLength < 0) 226 throw new IOException("invalid content length "+contentLength); 227 228 byte[] b = connection.getInputStream().readAllBytes(); 229 if (b.length != contentLength) 230 throw new EOFException("Expected:" + contentLength + ", read:" + b.length); 231 232 return b; 233 } 234 }