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