1/** 2 * Copyright 2006-2017 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.objenesis.instantiator.basic; 17 18/* 19 * Copyright 2003,2004 The Apache Software Foundation 20 * 21 * Licensed under the Apache License, Version 2.0 (the "License"); 22 * you may not use this file except in compliance with the License. 23 * You may obtain a copy of the License at 24 * 25 * http://www.apache.org/licenses/LICENSE-2.0 26 * 27 * Unless required by applicable law or agreed to in writing, software 28 * distributed under the License is distributed on an "AS IS" BASIS, 29 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 * See the License for the specific language governing permissions and 31 * limitations under the License. 32 */ 33 34import org.objenesis.ObjenesisException; 35 36import java.io.BufferedOutputStream; 37import java.io.FileOutputStream; 38import java.io.IOException; 39import java.io.InputStream; 40import java.lang.reflect.Method; 41import java.security.AccessController; 42import java.security.PrivilegedAction; 43import java.security.ProtectionDomain; 44 45/** 46 * Helper class for ProxyObjectInstantiator. We can see the details of a class specification 47 * <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html">here</a> 48 * 49 * @author Henri Tremblay 50 */ 51public final class ClassDefinitionUtils { 52 53 public static final byte OPS_aload_0 = 42; 54 public static final byte OPS_invokespecial = -73; // has two bytes parameters 55 public static final byte OPS_return = -79; 56 public static final byte OPS_new = -69; 57 public static final byte OPS_dup = 89; 58 public static final byte OPS_areturn = -80; 59 60 public static final int CONSTANT_Utf8 = 1; 61 public static final int CONSTANT_Integer = 3; 62 public static final int CONSTANT_Float = 4; 63 public static final int CONSTANT_Long = 5; 64 public static final int CONSTANT_Double = 6; 65 public static final int CONSTANT_Class = 7; 66 public static final int CONSTANT_String = 8; 67 public static final int CONSTANT_Fieldref = 9; 68 public static final int CONSTANT_Methodref = 10; 69 public static final int CONSTANT_InterfaceMethodref = 11; 70 public static final int CONSTANT_NameAndType = 12; 71 public static final int CONSTANT_MethodHandle = 15; 72 public static final int CONSTANT_MethodType = 16; 73 public static final int CONSTANT_InvokeDynamic = 18; 74 75 public static final int ACC_PUBLIC = 0x0001; // Declared public; may be accessed from outside its package. 76 public static final int ACC_FINAL = 0x0010; // Declared final; no subclasses allowed. 77 public static final int ACC_SUPER = 0x0020; // Treat superclass methods specially when invoked by the invokespecial instruction. 78 public static final int ACC_INTERFACE = 0x0200; // Is an interface, not a class. 79 public static final int ACC_ABSTRACT = 0x0400; // Declared abstract; must not be instantiated. 80 public static final int ACC_SYNTHETIC = 0x1000; // Declared synthetic; not present in the source code. 81 public static final int ACC_ANNOTATION = 0x2000; // Declared as an annotation type. 82 public static final int ACC_ENUM = 0x4000; // Declared as an enum type. 83 84 public static final byte[] MAGIC = { (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe }; 85 public static final byte[] VERSION = { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x31 }; // minor_version, major_version (Java 5) 86 87 private ClassDefinitionUtils() { } 88 89 private static Method DEFINE_CLASS; 90 private static final ProtectionDomain PROTECTION_DOMAIN; 91 92 static { 93 PROTECTION_DOMAIN = AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() { 94 public ProtectionDomain run() { 95 return ClassDefinitionUtils.class.getProtectionDomain(); 96 } 97 }); 98 99 AccessController.doPrivileged(new PrivilegedAction<Object>() { 100 public Object run() { 101 try { 102 Class<?> loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this 103 DEFINE_CLASS = loader.getDeclaredMethod("defineClass", 104 new Class[]{ String.class, 105 byte[].class, 106 Integer.TYPE, 107 Integer.TYPE, 108 ProtectionDomain.class }); 109 DEFINE_CLASS.setAccessible(true); 110 } catch (ClassNotFoundException e) { 111 throw new ObjenesisException(e); 112 } catch (NoSuchMethodException e) { 113 throw new ObjenesisException(e); 114 } 115 return null; 116 } 117 }); 118 } 119 120 /** 121 * Define a class in the provided class loader from the array of bytes. Inspired by cglib 122 * <code>ReflectUtils.defineClass</code> 123 * 124 * @param <T> type of the class returned 125 * @param className class name in the format <code>org.objenesis.MyClass</code> 126 * @param b bytes representing the class 127 * @param loader the class loader where the class will be loaded 128 * @return the newly loaded class 129 * @throws Exception whenever something goes wrong 130 */ 131 @SuppressWarnings("unchecked") 132 public static <T> Class<T> defineClass(String className, byte[] b, ClassLoader loader) 133 throws Exception { 134 Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), PROTECTION_DOMAIN }; 135 Class<T> c = (Class<T>) DEFINE_CLASS.invoke(loader, args); 136 // Force static initializers to run. 137 Class.forName(className, true, loader); 138 return c; 139 } 140 141 /** 142 * Read the bytes of a class from the classpath 143 * 144 * @param className full class name including the package 145 * @return the bytes representing the class 146 * @throws IllegalArgumentException if the class is longer than 2500 bytes 147 * @throws IOException if we fail to read the class 148 */ 149 public static byte[] readClass(String className) throws IOException { 150 // convert to a resource 151 className = classNameToResource(className); 152 153 byte[] b = new byte[2500]; // I'm assuming that I'm reading class that are not too big 154 155 int length; 156 157 InputStream in = ClassDefinitionUtils.class.getClassLoader().getResourceAsStream(className); 158 try { 159 length = in.read(b); 160 } 161 finally { 162 in.close(); 163 } 164 165 if(length >= 2500) { 166 throw new IllegalArgumentException("The class is longer that 2500 bytes which is currently unsupported"); 167 } 168 169 byte[] copy = new byte[length]; 170 System.arraycopy(b, 0, copy, 0, length); 171 return copy; 172 } 173 174 /** 175 * Write all class bytes to a file. 176 * 177 * @param fileName file where the bytes will be written 178 * @param bytes bytes representing the class 179 * @throws IOException if we fail to write the class 180 */ 181 public static void writeClass(String fileName, byte[] bytes) throws IOException { 182 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName)); 183 try { 184 out.write(bytes); 185 } 186 finally { 187 out.close(); 188 } 189 } 190 191 /** 192 * Will convert a class name to its name in the class definition format (e.g {@code org.objenesis.EmptyClass} 193 * becomes {@code org/objenesis/EmptyClass}) 194 * 195 * @param className full class name including the package 196 * @return the internal name 197 */ 198 public static String classNameToInternalClassName(String className) { 199 return className.replace('.', '/'); 200 } 201 202 /** 203 * Will convert a class name to its class loader resource name (e.g {@code org.objenesis.EmptyClass} 204 * becomes {@code org/objenesis/EmptyClass.class}) 205 * 206 * @param className full class name including the package 207 * @return the resource name 208 */ 209 public static String classNameToResource(String className) { 210 return classNameToInternalClassName(className) + ".class"; 211 } 212 213 /** 214 * Check if this class already exists in the class loader and return it if it does 215 * 216 * @param <T> type of the class returned 217 * @param classLoader Class loader where to search the class 218 * @param className Class name with full path 219 * @return the class if it already exists or null 220 */ 221 @SuppressWarnings("unchecked") 222 public static <T> Class<T> getExistingClass(ClassLoader classLoader, String className) { 223 try { 224 return (Class<T>) Class.forName(className, true, classLoader); 225 } 226 catch (ClassNotFoundException e) { 227 return null; 228 } 229 } 230} 231