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