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