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 */ 16 package org.objenesis.instantiator.sun; 17 18 import java.io.ByteArrayOutputStream; 19 import java.io.DataOutputStream; 20 import java.io.IOException; 21 22 import org.objenesis.ObjenesisException; 23 import org.objenesis.instantiator.ObjectInstantiator; 24 import org.objenesis.instantiator.annotations.Instantiator; 25 import org.objenesis.instantiator.annotations.Typology; 26 import org.objenesis.instantiator.basic.ClassDefinitionUtils; 27 28 import static org.objenesis.instantiator.basic.ClassDefinitionUtils.*; 29 30 /** 31 * This instantiator will correctly bypass the constructors by instantiating the class using the default 32 * constructor from Object. It will be allowed to do so by extending {@code MagicAccessorImpl} which prevents 33 * its children to be verified by the class loader 34 * 35 * @author Henri Tremblay 36 */ 37 @Instantiator(Typology.STANDARD) 38 public class MagicInstantiator<T> implements ObjectInstantiator<T> { 39 40 private static final int INDEX_CLASS_THIS = 1; 41 private static final int INDEX_CLASS_SUPERCLASS = 2; 42 private static final int INDEX_UTF8_CONSTRUCTOR_NAME = 3; 43 private static final int INDEX_UTF8_CONSTRUCTOR_DESC = 4; 44 private static final int INDEX_UTF8_CODE_ATTRIBUTE = 5; 45 private static final int INDEX_UTF8_INSTANTIATOR_CLASS = 7; 46 private static final int INDEX_UTF8_SUPERCLASS = 8; 47 private static final int INDEX_CLASS_INTERFACE = 9; 48 private static final int INDEX_UTF8_INTERFACE = 10; 49 private static final int INDEX_UTF8_NEWINSTANCE_NAME = 11; 50 private static final int INDEX_UTF8_NEWINSTANCE_DESC = 12; 51 private static final int INDEX_METHODREF_OBJECT_CONSTRUCTOR = 13; 52 private static final int INDEX_CLASS_OBJECT = 14; 53 private static final int INDEX_UTF8_OBJECT = 15; 54 private static final int INDEX_NAMEANDTYPE_DEFAULT_CONSTRUCTOR = 16; 55 private static final int INDEX_CLASS_TYPE = 17; 56 private static final int INDEX_UTF8_TYPE = 18; 57 58 private static int CONSTANT_POOL_COUNT = 19; 59 60 private static final byte[] CONSTRUCTOR_CODE = { OPS_aload_0, OPS_invokespecial, 0, INDEX_METHODREF_OBJECT_CONSTRUCTOR, OPS_return}; 61 private static final int CONSTRUCTOR_CODE_ATTRIBUTE_LENGTH = 12 + CONSTRUCTOR_CODE.length; 62 63 private static final byte[] NEWINSTANCE_CODE = { OPS_new, 0, INDEX_CLASS_TYPE, OPS_dup, OPS_invokespecial, 0, INDEX_METHODREF_OBJECT_CONSTRUCTOR, OPS_areturn}; 64 private static final int NEWINSTANCE_CODE_ATTRIBUTE_LENGTH = 12 + NEWINSTANCE_CODE.length; 65 66 private static final String CONSTRUCTOR_NAME = "<init>"; 67 private static final String CONSTRUCTOR_DESC = "()V"; 68 69 private ObjectInstantiator<T> instantiator; 70 71 public MagicInstantiator(Class<T> type) { 72 instantiator = newInstantiatorOf(type); 73 } 74 75 /** 76 * Get the underlying instantiator. 77 * 78 * {@link MagicInstantiator} is a wrapper around another object 79 * which implements {@link ObjectInstantiator} interface. 80 * This method exposes that instantiator. 81 * 82 * @return the underlying instantiator 83 */ 84 public ObjectInstantiator<T> getInstantiator() { 85 return instantiator; 86 } 87 88 private <T> ObjectInstantiator<T> newInstantiatorOf(Class<T> type) { 89 String suffix = type.getSimpleName(); 90 String className = getClass().getName() + "$$$" + suffix; 91 92 Class<ObjectInstantiator<T>> clazz = getExistingClass(getClass().getClassLoader(), className); 93 94 if(clazz == null) { 95 byte[] classBytes = writeExtendingClass(type, className); 96 97 try { 98 clazz = ClassDefinitionUtils.defineClass(className, classBytes, getClass().getClassLoader()); 99 } catch (Exception e) { 100 throw new ObjenesisException(e); 101 } 102 } 103 104 try { 105 return clazz.newInstance(); 106 } catch (InstantiationException e) { 107 throw new ObjenesisException(e); 108 } catch (IllegalAccessException e) { 109 throw new ObjenesisException(e); 110 } 111 } 112 113 /** 114 * Will generate the bytes for a class extending the type passed in parameter. This class will 115 * only have an empty default constructor 116 * 117 * @param type type to extend 118 * @param className name of the wrapped instantiator class 119 * @return the byte for the class 120 * @throws ObjenesisException is something goes wrong 121 */ 122 private byte[] writeExtendingClass(Class<?> type, String className) { 123 String clazz = classNameToInternalClassName(className); 124 125 DataOutputStream in = null; 126 ByteArrayOutputStream bIn = new ByteArrayOutputStream(1000); // 1000 should be large enough to fit the entire class 127 try { 128 in = new DataOutputStream(bIn); 129 130 in.write(MAGIC); 131 in.write(VERSION); 132 in.writeShort(CONSTANT_POOL_COUNT); 133 134 // set all the constant pool here 135 136 // 1. class 137 in.writeByte(CONSTANT_Class); 138 in.writeShort(INDEX_UTF8_INSTANTIATOR_CLASS); 139 140 // 2. super class 141 in.writeByte(CONSTANT_Class); 142 in.writeShort(INDEX_UTF8_SUPERCLASS); 143 144 // 3. default constructor name 145 in.writeByte(CONSTANT_Utf8); 146 in.writeUTF(CONSTRUCTOR_NAME); 147 148 // 4. default constructor description 149 in.writeByte(CONSTANT_Utf8); 150 in.writeUTF(CONSTRUCTOR_DESC); 151 152 // 5. Code 153 in.writeByte(CONSTANT_Utf8); 154 in.writeUTF("Code"); 155 156 // 6. Class name 157 in.writeByte(CONSTANT_Utf8); 158 in.writeUTF("L" + clazz + ";"); 159 160 // 7. Class name (again) 161 in.writeByte(CONSTANT_Utf8); 162 in.writeUTF(clazz); 163 164 // 8. Superclass name 165 in.writeByte(CONSTANT_Utf8); 166 // in.writeUTF("java/lang/Object"); 167 in.writeUTF("sun/reflect/MagicAccessorImpl"); 168 169 // 9. ObjectInstantiator interface 170 in.writeByte(CONSTANT_Class); 171 in.writeShort(INDEX_UTF8_INTERFACE); 172 173 // 10. ObjectInstantiator name 174 in.writeByte(CONSTANT_Utf8); 175 in.writeUTF(ObjectInstantiator.class.getName().replace('.', '/')); 176 177 // 11. newInstance name 178 in.writeByte(CONSTANT_Utf8); 179 in.writeUTF("newInstance"); 180 181 // 12. newInstance desc 182 in.writeByte(CONSTANT_Utf8); 183 in.writeUTF("()Ljava/lang/Object;"); 184 185 // 13. Methodref to the Object constructor 186 in.writeByte(CONSTANT_Methodref); 187 in.writeShort(INDEX_CLASS_OBJECT); 188 in.writeShort(INDEX_NAMEANDTYPE_DEFAULT_CONSTRUCTOR); 189 190 // 14. Object class 191 in.writeByte(CONSTANT_Class); 192 in.writeShort(INDEX_UTF8_OBJECT); 193 194 // 15. Object class name 195 in.writeByte(CONSTANT_Utf8); 196 in.writeUTF("java/lang/Object"); 197 198 // 16. Default constructor name and type 199 in.writeByte(CONSTANT_NameAndType); 200 in.writeShort(INDEX_UTF8_CONSTRUCTOR_NAME); 201 in.writeShort(INDEX_UTF8_CONSTRUCTOR_DESC); 202 203 // 17. Type to instantiate class 204 in.writeByte(CONSTANT_Class); 205 in.writeShort(INDEX_UTF8_TYPE); 206 207 // 18. Type to instantiate name 208 in.writeByte(CONSTANT_Utf8); 209 in.writeUTF(classNameToInternalClassName(type.getName())); 210 211 // end of constant pool 212 213 // access flags: We want public, ACC_SUPER is always there 214 in.writeShort(ACC_PUBLIC | ACC_SUPER | ACC_FINAL); 215 216 // this class index in the constant pool 217 in.writeShort(INDEX_CLASS_THIS); 218 219 // super class index in the constant pool 220 in.writeShort(INDEX_CLASS_SUPERCLASS); 221 222 // interfaces implemented count (we have none) 223 in.writeShort(1); 224 in.writeShort(INDEX_CLASS_INTERFACE); 225 226 // fields count (we have none) 227 in.writeShort(0); 228 229 // method count (we have two: the default constructor and newInstance) 230 in.writeShort(2); 231 232 // default constructor method_info 233 in.writeShort(ACC_PUBLIC); 234 in.writeShort(INDEX_UTF8_CONSTRUCTOR_NAME); // index of the method name (<init>) 235 in.writeShort(INDEX_UTF8_CONSTRUCTOR_DESC); // index of the description 236 in.writeShort(1); // number of attributes: only one, the code 237 238 // code attribute of the default constructor 239 in.writeShort(INDEX_UTF8_CODE_ATTRIBUTE); 240 in.writeInt(CONSTRUCTOR_CODE_ATTRIBUTE_LENGTH); // attribute length 241 in.writeShort(0); // max_stack 242 in.writeShort(1); // max_locals 243 in.writeInt(CONSTRUCTOR_CODE.length); // code length 244 in.write(CONSTRUCTOR_CODE); 245 in.writeShort(0); // exception_table_length = 0 246 in.writeShort(0); // attributes count = 0, no need to have LineNumberTable and LocalVariableTable 247 248 // newInstance method_info 249 in.writeShort(ACC_PUBLIC); 250 in.writeShort(INDEX_UTF8_NEWINSTANCE_NAME); // index of the method name (newInstance) 251 in.writeShort(INDEX_UTF8_NEWINSTANCE_DESC); // index of the description 252 in.writeShort(1); // number of attributes: only one, the code 253 254 // code attribute of newInstance 255 in.writeShort(INDEX_UTF8_CODE_ATTRIBUTE); 256 in.writeInt(NEWINSTANCE_CODE_ATTRIBUTE_LENGTH); // attribute length 257 in.writeShort(2); // max_stack 258 in.writeShort(1); // max_locals 259 in.writeInt(NEWINSTANCE_CODE.length); // code length 260 in.write(NEWINSTANCE_CODE); 261 in.writeShort(0); // exception_table_length = 0 262 in.writeShort(0); // attributes count = 0, no need to have LineNumberTable and LocalVariableTable 263 264 // class attributes 265 in.writeShort(0); // none. No need to have a source file attribute 266 267 } catch (IOException e) { 268 throw new ObjenesisException(e); 269 } finally { 270 if(in != null) { 271 try { 272 in.close(); 273 } catch (IOException e) { 274 throw new ObjenesisException(e); 275 } 276 } 277 } 278 279 return bIn.toByteArray(); 280 } 281 282 public T newInstance() { 283 return instantiator.newInstance(); 284 } 285 } 286