Home | History | Annotate | Download | only in javassist
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist;
     17 
     18 import javassist.bytecode.*;
     19 import javassist.compiler.Javac;
     20 import javassist.compiler.CompileError;
     21 
     22 /**
     23  * An instance of CtConstructor represents a constructor.
     24  * It may represent a static constructor
     25  * (class initializer).  To distinguish a constructor and a class
     26  * initializer, call <code>isClassInitializer()</code>.
     27  *
     28  * <p>See the super class <code>CtBehavior</code> as well since
     29  * a number of useful methods are in <code>CtBehavior</code>.
     30  *
     31  * @see CtClass#getDeclaredConstructors()
     32  * @see CtClass#getClassInitializer()
     33  * @see CtNewConstructor
     34  */
     35 public final class CtConstructor extends CtBehavior {
     36     protected CtConstructor(MethodInfo minfo, CtClass declaring) {
     37         super(declaring, minfo);
     38     }
     39 
     40     /**
     41      * Creates a constructor with no constructor body.
     42      * The created constructor
     43      * must be added to a class with <code>CtClass.addConstructor()</code>.
     44      *
     45      * <p>The created constructor does not include a constructor body,
     46      * which must be specified with <code>setBody()</code>.
     47      *
     48      * @param declaring         the class to which the created method is added.
     49      * @param parameters        a list of the parameter types
     50      *
     51      * @see CtClass#addConstructor(CtConstructor)
     52      * @see CtConstructor#setBody(String)
     53      * @see CtConstructor#setBody(CtConstructor,ClassMap)
     54      */
     55     public CtConstructor(CtClass[] parameters, CtClass declaring) {
     56         this((MethodInfo)null, declaring);
     57         ConstPool cp = declaring.getClassFile2().getConstPool();
     58         String desc = Descriptor.ofConstructor(parameters);
     59         methodInfo = new MethodInfo(cp, "<init>", desc);
     60         setModifiers(Modifier.PUBLIC);
     61     }
     62 
     63     /**
     64      * Creates a copy of a <code>CtConstructor</code> object.
     65      * The created constructor must be
     66      * added to a class with <code>CtClass.addConstructor()</code>.
     67      *
     68      * <p>All occurrences of class names in the created constructor
     69      * are replaced with names specified by
     70      * <code>map</code> if <code>map</code> is not <code>null</code>.
     71      *
     72      * <p>By default, all the occurrences of the names of the class
     73      * declaring <code>src</code> and the superclass are replaced
     74      * with the name of the class and the superclass that
     75      * the created constructor is added to.
     76      * This is done whichever <code>map</code> is null or not.
     77      * To prevent this replacement, call <code>ClassMap.fix()</code>
     78      * or <code>put()</code> to explicitly specify replacement.
     79      *
     80      * <p><b>Note:</b> if the <code>.class</code> notation (for example,
     81      * <code>String.class</code>) is included in an expression, the
     82      * Javac compiler may produce a helper method.
     83      * Since this constructor never
     84      * copies this helper method, the programmers have the responsiblity of
     85      * copying it.  Otherwise, use <code>Class.forName()</code> in the
     86      * expression.
     87      *
     88      * @param src       the source method.
     89      * @param declaring    the class to which the created method is added.
     90      * @param map       the hashtable associating original class names
     91      *                  with substituted names.
     92      *                  It can be <code>null</code>.
     93      *
     94      * @see CtClass#addConstructor(CtConstructor)
     95      * @see ClassMap#fix(String)
     96      */
     97     public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map)
     98         throws CannotCompileException
     99     {
    100         this((MethodInfo)null, declaring);
    101         copy(src, true, map);
    102     }
    103 
    104     /**
    105      * Returns true if this object represents a constructor.
    106      */
    107     public boolean isConstructor() {
    108         return methodInfo.isConstructor();
    109     }
    110 
    111     /**
    112      * Returns true if this object represents a static initializer.
    113      */
    114     public boolean isClassInitializer() {
    115         return methodInfo.isStaticInitializer();
    116     }
    117 
    118     /**
    119      * Returns the constructor name followed by parameter types
    120      * such as <code>javassist.CtConstructor(CtClass[],CtClass)</code>.
    121      *
    122      * @since 3.5
    123      */
    124     public String getLongName() {
    125         return getDeclaringClass().getName()
    126                + (isConstructor() ? Descriptor.toString(getSignature())
    127                                   : ("." + MethodInfo.nameClinit + "()"));
    128     }
    129 
    130     /**
    131      * Obtains the name of this constructor.
    132      * It is the same as the simple name of the class declaring this
    133      * constructor.  If this object represents a class initializer,
    134      * then this method returns <code>"&lt;clinit&gt;"</code>.
    135      */
    136     public String getName() {
    137         if (methodInfo.isStaticInitializer())
    138             return MethodInfo.nameClinit;
    139         else
    140             return declaringClass.getSimpleName();
    141     }
    142 
    143     /**
    144      * Returns true if the constructor (or static initializer)
    145      * is the default one.  This method returns true if the constructor
    146      * takes some arguments but it does not perform anything except
    147      * calling <code>super()</code> (the no-argument constructor of
    148      * the super class).
    149      */
    150     public boolean isEmpty() {
    151         CodeAttribute ca = getMethodInfo2().getCodeAttribute();
    152         if (ca == null)
    153             return false;       // native or abstract??
    154                                 // they are not allowed, though.
    155 
    156         ConstPool cp = ca.getConstPool();
    157         CodeIterator it = ca.iterator();
    158         try {
    159             int pos, desc;
    160             int op0 = it.byteAt(it.next());
    161             return op0 == Opcode.RETURN     // empty static initializer
    162                 || (op0 == Opcode.ALOAD_0
    163                     && it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL
    164                     && (desc = cp.isConstructor(getSuperclassName(),
    165                                                 it.u16bitAt(pos + 1))) != 0
    166                     && "()V".equals(cp.getUtf8Info(desc))
    167                     && it.byteAt(it.next()) == Opcode.RETURN
    168                     && !it.hasNext());
    169         }
    170         catch (BadBytecode e) {}
    171         return false;
    172     }
    173 
    174     private String getSuperclassName() {
    175         ClassFile cf = declaringClass.getClassFile2();
    176         return cf.getSuperclass();
    177     }
    178 
    179     /**
    180      * Returns true if this constructor calls a constructor
    181      * of the super class.  This method returns false if it
    182      * calls another constructor of this class by <code>this()</code>.
    183      */
    184     public boolean callsSuper() throws CannotCompileException {
    185         CodeAttribute codeAttr = methodInfo.getCodeAttribute();
    186         if (codeAttr != null) {
    187             CodeIterator it = codeAttr.iterator();
    188             try {
    189                 int index = it.skipSuperConstructor();
    190                 return index >= 0;
    191             }
    192             catch (BadBytecode e) {
    193                 throw new CannotCompileException(e);
    194             }
    195         }
    196 
    197         return false;
    198     }
    199 
    200     /**
    201      * Sets a constructor body.
    202      *
    203      * @param src       the source code representing the constructor body.
    204      *                  It must be a single statement or block.
    205      *                  If it is <code>null</code>, the substituted
    206      *                  constructor body does nothing except calling
    207      *                  <code>super()</code>.
    208      */
    209     public void setBody(String src) throws CannotCompileException {
    210         if (src == null)
    211             if (isClassInitializer())
    212                 src = ";";
    213             else
    214                 src = "super();";
    215 
    216         super.setBody(src);
    217     }
    218 
    219     /**
    220      * Copies a constructor body from another constructor.
    221      *
    222      * <p>All occurrences of the class names in the copied body
    223      * are replaced with the names specified by
    224      * <code>map</code> if <code>map</code> is not <code>null</code>.
    225      *
    226      * @param src       the method that the body is copied from.
    227      * @param map       the hashtable associating original class names
    228      *                  with substituted names.
    229      *                  It can be <code>null</code>.
    230      */
    231     public void setBody(CtConstructor src, ClassMap map)
    232         throws CannotCompileException
    233     {
    234         setBody0(src.declaringClass, src.methodInfo,
    235                  declaringClass, methodInfo, map);
    236     }
    237 
    238     /**
    239      * Inserts bytecode just after another constructor in the super class
    240      * or this class is called.
    241      * It does not work if this object represents a class initializer.
    242      *
    243      * @param src       the source code representing the inserted bytecode.
    244      *                  It must be a single statement or block.
    245      */
    246     public void insertBeforeBody(String src) throws CannotCompileException {
    247         CtClass cc = declaringClass;
    248         cc.checkModify();
    249         if (isClassInitializer())
    250             throw new CannotCompileException("class initializer");
    251 
    252         CodeAttribute ca = methodInfo.getCodeAttribute();
    253         CodeIterator iterator = ca.iterator();
    254         Bytecode b = new Bytecode(methodInfo.getConstPool(),
    255                                   ca.getMaxStack(), ca.getMaxLocals());
    256         b.setStackDepth(ca.getMaxStack());
    257         Javac jv = new Javac(b, cc);
    258         try {
    259             jv.recordParams(getParameterTypes(), false);
    260             jv.compileStmnt(src);
    261             ca.setMaxStack(b.getMaxStack());
    262             ca.setMaxLocals(b.getMaxLocals());
    263             iterator.skipConstructor();
    264             int pos = iterator.insertEx(b.get());
    265             iterator.insert(b.getExceptionTable(), pos);
    266             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
    267         }
    268         catch (NotFoundException e) {
    269             throw new CannotCompileException(e);
    270         }
    271         catch (CompileError e) {
    272             throw new CannotCompileException(e);
    273         }
    274         catch (BadBytecode e) {
    275             throw new CannotCompileException(e);
    276         }
    277     }
    278 
    279     /* This method is called by addCatch() in CtBehavior.
    280      * super() and this() must not be in a try statement.
    281      */
    282     int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
    283         CodeIterator ci = ca.iterator();
    284         try {
    285             ci.skipConstructor();
    286             return ci.next();
    287         }
    288         catch (BadBytecode e) {
    289             throw new CannotCompileException(e);
    290         }
    291     }
    292 
    293     /**
    294      * Makes a copy of this constructor and converts it into a method.
    295      * The signature of the mehtod is the same as the that of this constructor.
    296      * The return type is <code>void</code>.  The resulting method must be
    297      * appended to the class specified by <code>declaring</code>.
    298      * If this constructor is a static initializer, the resulting method takes
    299      * no parameter.
    300      *
    301      * <p>An occurrence of another constructor call <code>this()</code>
    302      * or a super constructor call <code>super()</code> is
    303      * eliminated from the resulting method.
    304      *
    305      * <p>The immediate super class of the class declaring this constructor
    306      * must be also a super class of the class declaring the resulting method.
    307      * If the constructor accesses a field, the class declaring the resulting method
    308      * must also declare a field with the same name and type.
    309      *
    310      * @param name              the name of the resulting method.
    311      * @param declaring         the class declaring the resulting method.
    312      */
    313     public CtMethod toMethod(String name, CtClass declaring)
    314         throws CannotCompileException
    315     {
    316         return toMethod(name, declaring, null);
    317     }
    318 
    319     /**
    320      * Makes a copy of this constructor and converts it into a method.
    321      * The signature of the method is the same as the that of this constructor.
    322      * The return type is <code>void</code>.  The resulting method must be
    323      * appended to the class specified by <code>declaring</code>.
    324      * If this constructor is a static initializer, the resulting method takes
    325      * no parameter.
    326      *
    327      * <p>An occurrence of another constructor call <code>this()</code>
    328      * or a super constructor call <code>super()</code> is
    329      * eliminated from the resulting method.
    330      *
    331      * <p>The immediate super class of the class declaring this constructor
    332      * must be also a super class of the class declaring the resulting method
    333      * (this is obviously true if the second parameter <code>declaring</code> is
    334      * the same as the class declaring this constructor).
    335      * If the constructor accesses a field, the class declaring the resulting method
    336      * must also declare a field with the same name and type.
    337      *
    338      * @param name              the name of the resulting method.
    339      * @param declaring         the class declaring the resulting method.
    340      *                          It is normally the same as the class declaring this
    341      *                          constructor.
    342      * @param map       the hash table associating original class names
    343      *                  with substituted names.  The original class names will be
    344      *                  replaced while making a copy.
    345      *                  <code>map</code> can be <code>null</code>.
    346      */
    347     public CtMethod toMethod(String name, CtClass declaring, ClassMap map)
    348         throws CannotCompileException
    349     {
    350         CtMethod method = new CtMethod(null, declaring);
    351         method.copy(this, false, map);
    352         if (isConstructor()) {
    353             MethodInfo minfo = method.getMethodInfo2();
    354             CodeAttribute ca = minfo.getCodeAttribute();
    355             if (ca != null) {
    356                 removeConsCall(ca);
    357                 try {
    358                     methodInfo.rebuildStackMapIf6(declaring.getClassPool(),
    359                                                   declaring.getClassFile2());
    360                 }
    361                 catch (BadBytecode e) {
    362                     throw new CannotCompileException(e);
    363                 }
    364             }
    365         }
    366 
    367         method.setName(name);
    368         return method;
    369     }
    370 
    371     private static void removeConsCall(CodeAttribute ca)
    372         throws CannotCompileException
    373     {
    374         CodeIterator iterator = ca.iterator();
    375         try {
    376             int pos = iterator.skipConstructor();
    377             if (pos >= 0) {
    378                 int mref = iterator.u16bitAt(pos + 1);
    379                 String desc = ca.getConstPool().getMethodrefType(mref);
    380                 int num = Descriptor.numOfParameters(desc) + 1;
    381                 if (num > 3)
    382                     pos = iterator.insertGapAt(pos, num - 3, false).position;
    383 
    384                 iterator.writeByte(Opcode.POP, pos++);  // this
    385                 iterator.writeByte(Opcode.NOP, pos);
    386                 iterator.writeByte(Opcode.NOP, pos + 1);
    387                 Descriptor.Iterator it = new Descriptor.Iterator(desc);
    388                 while (true) {
    389                     it.next();
    390                     if (it.isParameter())
    391                         iterator.writeByte(it.is2byte() ? Opcode.POP2 : Opcode.POP,
    392                                            pos++);
    393                     else
    394                         break;
    395                 }
    396             }
    397         }
    398         catch (BadBytecode e) {
    399             throw new CannotCompileException(e);
    400         }
    401     }
    402 }
    403