Home | History | Annotate | Download | only in sun
      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