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 18import org.objenesis.ObjenesisException; 19import org.objenesis.instantiator.ObjectInstantiator; 20import org.objenesis.instantiator.annotations.Instantiator; 21import org.objenesis.instantiator.annotations.Typology; 22 23import java.io.ByteArrayOutputStream; 24import java.io.DataOutputStream; 25import java.io.IOException; 26 27import static org.objenesis.instantiator.basic.ClassDefinitionUtils.*; 28 29/** 30 * This instantiator creates a class by dynamically extending it. It will skip the call to the parent constructor 31 * in the bytecode. So that the constructor is indeed not called but you however instantiate a child class, not 32 * the actual class. The class loader will normally throw a {@code VerifyError} is you do that. However, using 33 * {@code -Xverify:none} shoud make it work 34 * 35 * @author Henri Tremblay 36 */ 37@Instantiator(Typology.STANDARD) 38public class ProxyingInstantiator<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_CLASS = 7; 46 private static final int INDEX_UTF8_SUPERCLASS = 8; 47 48 private static int CONSTANT_POOL_COUNT = 9; 49 50 private static final byte[] CODE = { OPS_aload_0, OPS_return}; 51 private static final int CODE_ATTRIBUTE_LENGTH = 12 + CODE.length; 52 53 private static final String SUFFIX = "$$$Objenesis"; 54 55 private static final String CONSTRUCTOR_NAME = "<init>"; 56 private static final String CONSTRUCTOR_DESC = "()V"; 57 58 private final Class<?> newType; 59 60 public ProxyingInstantiator(Class<T> type) { 61 62 byte[] classBytes = writeExtendingClass(type, SUFFIX); 63 64 try { 65 newType = ClassDefinitionUtils.defineClass(type.getName() + SUFFIX, classBytes, type.getClassLoader()); 66 } catch (Exception e) { 67 throw new ObjenesisException(e); 68 } 69 } 70 71 @SuppressWarnings("unchecked") 72 public T newInstance() { 73 try { 74 return (T) newType.newInstance(); 75 } catch (InstantiationException e) { 76 throw new ObjenesisException(e); 77 } catch (IllegalAccessException e) { 78 throw new ObjenesisException(e); 79 } 80 } 81 82 /** 83 * Will generate the bytes for a class extending the type passed in parameter. This class will 84 * only have an empty default constructor 85 * 86 * @param type type to extend 87 * @param suffix the suffix appended to the class name to create the next extending class name 88 * @return the byte for the class 89 * @throws ObjenesisException is something goes wrong 90 */ 91 private static byte[] writeExtendingClass(Class<?> type, String suffix) { 92 String parentClazz = classNameToInternalClassName(type.getName()); 93 String clazz = parentClazz + suffix; 94 95 DataOutputStream in = null; 96 ByteArrayOutputStream bIn = new ByteArrayOutputStream(1000); // 1000 should be large enough to fit the entire class 97 try { 98 in = new DataOutputStream(bIn); 99 100 in.write(MAGIC); 101 in.write(VERSION); 102 in.writeShort(CONSTANT_POOL_COUNT); 103 104 // set all the constant pool here 105 106 // 1. class 107 in.writeByte(CONSTANT_Class); 108 in.writeShort(INDEX_UTF8_CLASS); 109 110 // 2. super class 111 in.writeByte(CONSTANT_Class); 112 in.writeShort(INDEX_UTF8_SUPERCLASS); 113 114 // 3. default constructor name 115 in.writeByte(CONSTANT_Utf8); 116 in.writeUTF(CONSTRUCTOR_NAME); 117 118 // 4. default constructor description 119 in.writeByte(CONSTANT_Utf8); 120 in.writeUTF(CONSTRUCTOR_DESC); 121 122 // 5. Code 123 in.writeByte(CONSTANT_Utf8); 124 in.writeUTF("Code"); 125 126 // 6. Class name 127 in.writeByte(CONSTANT_Utf8); 128 in.writeUTF("L" + clazz + ";"); 129 130 // 7. Class name (again) 131 in.writeByte(CONSTANT_Utf8); 132 in.writeUTF(clazz); 133 134 // 8. Superclass name 135 in.writeByte(CONSTANT_Utf8); 136 in.writeUTF(parentClazz); 137 138 // end of constant pool 139 140 // access flags: We want public, ACC_SUPER is always there 141 in.writeShort(ACC_PUBLIC | ACC_SUPER); 142 143 // this class index in the constant pool 144 in.writeShort(INDEX_CLASS_THIS); 145 146 // super class index in the constant pool 147 in.writeShort(INDEX_CLASS_SUPERCLASS); 148 149 // interfaces implemented count (we have none) 150 in.writeShort(0); 151 152 // fields count (we have none) 153 in.writeShort(0); 154 155 // methods count (we have one: the default constructor) 156 in.writeShort(1); 157 158 // default constructor method_info 159 in.writeShort(ACC_PUBLIC); 160 in.writeShort(INDEX_UTF8_CONSTRUCTOR_NAME); // index of the method name (<init>) 161 in.writeShort(INDEX_UTF8_CONSTRUCTOR_DESC); // index of the description 162 in.writeShort(1); // number of attributes: only one, the code 163 164 // code attribute of the default constructor 165 in.writeShort(INDEX_UTF8_CODE_ATTRIBUTE); 166 in.writeInt(CODE_ATTRIBUTE_LENGTH); // attribute length 167 in.writeShort(1); // max_stack 168 in.writeShort(1); // max_locals 169 in.writeInt(CODE.length); // code length 170 in.write(CODE); 171 in.writeShort(0); // exception_table_length = 0 172 in.writeShort(0); // attributes count = 0, no need to have LineNumberTable and LocalVariableTable 173 174 // class attributes 175 in.writeShort(0); // none. No need to have a source file attribute 176 177 178 } catch (IOException e) { 179 throw new ObjenesisException(e); 180 } finally { 181 if(in != null) { 182 try { 183 in.close(); 184 } catch (IOException e) { 185 throw new ObjenesisException(e); 186 } 187 } 188 } 189 190 return bIn.toByteArray(); 191 } 192} 193