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.sun;
17
18import java.io.ByteArrayOutputStream;
19import java.io.DataOutputStream;
20import java.io.IOException;
21
22import org.objenesis.ObjenesisException;
23import org.objenesis.instantiator.ObjectInstantiator;
24import org.objenesis.instantiator.annotations.Instantiator;
25import org.objenesis.instantiator.annotations.Typology;
26import org.objenesis.instantiator.basic.ClassDefinitionUtils;
27
28import 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)
38public 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