001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist;
017:
018: import javassist.bytecode.*;
019: import javassist.compiler.Javac;
020: import javassist.compiler.CompileError;
021:
022: /**
023: * An instance of CtConstructor represents a constructor.
024: * It may represent a static constructor
025: * (class initializer). To distinguish a constructor and a class
026: * initializer, call <code>isClassInitializer()</code>.
027: *
028: * <p>See the super class <code>CtBehavior</code> as well since
029: * a number of useful methods are in <code>CtBehavior</code>.
030: *
031: * @see CtClass#getDeclaredConstructors()
032: * @see CtClass#getClassInitializer()
033: * @see CtNewConstructor
034: */
035: public final class CtConstructor extends CtBehavior {
036: protected CtConstructor(MethodInfo minfo, CtClass declaring) {
037: super (declaring, minfo);
038: next = null;
039: }
040:
041: /**
042: * Creates a constructor with no constructor body.
043: * The created constructor
044: * must be added to a class with <code>CtClass.addConstructor()</code>.
045: *
046: * <p>The created constructor does not include a constructor body,
047: * must be specified with <code>setBody()</code>.
048: *
049: * @param declaring the class to which the created method is added.
050: * @param parameters a list of the parameter types
051: *
052: * @see CtClass#addConstructor(CtConstructor)
053: * @see CtConstructor#setBody(String)
054: * @see CtConstructor#setBody(CtConstructor,ClassMap)
055: */
056: public CtConstructor(CtClass[] parameters, CtClass declaring) {
057: this ((MethodInfo) null, declaring);
058: ConstPool cp = declaring.getClassFile2().getConstPool();
059: String desc = Descriptor.ofConstructor(parameters);
060: methodInfo = new MethodInfo(cp, "<init>", desc);
061: setModifiers(Modifier.PUBLIC);
062: }
063:
064: /**
065: * Creates a copy of a <code>CtConstructor</code> object.
066: * The created constructor must be
067: * added to a class with <code>CtClass.addConstructor()</code>.
068: *
069: * <p>All occurrences of class names in the created constructor
070: * are replaced with names specified by
071: * <code>map</code> if <code>map</code> is not <code>null</code>.
072: *
073: * <p>By default, all the occurrences of the names of the class
074: * declaring <code>src</code> and the superclass are replaced
075: * with the name of the class and the superclass that
076: * the created constructor is added to.
077: * This is done whichever <code>map</code> is null or not.
078: * To prevent this replacement, call <code>ClassMap.fix()</code>.
079: *
080: * <p><b>Note:</b> if the <code>.class</code> notation (for example,
081: * <code>String.class</code>) is included in an expression, the
082: * Javac compiler may produce a helper method.
083: * Since this constructor never
084: * copies this helper method, the programmers have the responsiblity of
085: * copying it. Otherwise, use <code>Class.forName()</code> in the
086: * expression.
087: *
088: * @param src the source method.
089: * @param declaring the class to which the created method is added.
090: * @param map the hashtable associating original class names
091: * with substituted names.
092: * It can be <code>null</code>.
093: *
094: * @see CtClass#addConstructor(CtConstructor)
095: * @see ClassMap#fix(String)
096: */
097: public CtConstructor(CtConstructor src, CtClass declaring,
098: ClassMap map) throws CannotCompileException {
099: this ((MethodInfo) null, declaring);
100: copy(src, true, map);
101: }
102:
103: /**
104: * Returns true if this object represents a constructor.
105: */
106: public boolean isConstructor() {
107: return methodInfo.isConstructor();
108: }
109:
110: /**
111: * Returns true if this object represents a static initializer.
112: */
113: public boolean isClassInitializer() {
114: return methodInfo.isStaticInitializer();
115: }
116:
117: /**
118: * Obtains the name of this constructor.
119: * It is the same as the simple name of the class declaring this
120: * constructor. If this object represents a class initializer,
121: * then this method returns <code>"<clinit>"</code>.
122: */
123: public String getName() {
124: if (methodInfo.isStaticInitializer())
125: return MethodInfo.nameClinit;
126: else
127: return declaringClass.getSimpleName();
128: }
129:
130: /**
131: * Returns true if the constructor (or static initializer)
132: * is the default one. This method returns true if the constructor
133: * takes some arguments but it does not perform anything except
134: * calling <code>super()</code> (the no-argument constructor of
135: * the super class).
136: */
137: public boolean isEmpty() {
138: CodeAttribute ca = getMethodInfo2().getCodeAttribute();
139: if (ca == null)
140: return false; // native or abstract??
141: // they are not allowed, though.
142:
143: ConstPool cp = ca.getConstPool();
144: CodeIterator it = ca.iterator();
145: try {
146: int pos, desc;
147: int op0 = it.byteAt(it.next());
148: return op0 == Opcode.RETURN // empty static initializer
149: || (op0 == Opcode.ALOAD_0
150: && it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL
151: && (desc = cp.isConstructor(
152: getSuperclassName(), it
153: .u16bitAt(pos + 1))) != 0
154: && "()V".equals(cp.getUtf8Info(desc))
155: && it.byteAt(it.next()) == Opcode.RETURN && !it
156: .hasNext());
157: } catch (BadBytecode e) {
158: }
159: return false;
160: }
161:
162: private String getSuperclassName() {
163: ClassFile cf = declaringClass.getClassFile2();
164: return cf.getSuperclass();
165: }
166:
167: /**
168: * Returns true if this constructor calls a constructor
169: * of the super class. This method returns false if it
170: * calls another constructor of this class by <code>this()</code>.
171: */
172: public boolean callsSuper() throws CannotCompileException {
173: CodeAttribute codeAttr = methodInfo.getCodeAttribute();
174: if (codeAttr != null) {
175: CodeIterator it = codeAttr.iterator();
176: try {
177: int index = it.skipSuperConstructor();
178: return index >= 0;
179: } catch (BadBytecode e) {
180: throw new CannotCompileException(e);
181: }
182: }
183:
184: return false;
185: }
186:
187: /**
188: * Sets a constructor body.
189: *
190: * @param src the source code representing the constructor body.
191: * It must be a single statement or block.
192: * If it is <code>null</code>, the substituted
193: * constructor body does nothing except calling
194: * <code>super()</code>.
195: */
196: public void setBody(String src) throws CannotCompileException {
197: if (src == null)
198: if (isClassInitializer())
199: src = ";";
200: else
201: src = "super();";
202:
203: super .setBody(src);
204: }
205:
206: /**
207: * Copies a constructor body from another constructor.
208: *
209: * <p>All occurrences of the class names in the copied body
210: * are replaced with the names specified by
211: * <code>map</code> if <code>map</code> is not <code>null</code>.
212: *
213: * @param src the method that the body is copied from.
214: * @param map the hashtable associating original class names
215: * with substituted names.
216: * It can be <code>null</code>.
217: */
218: public void setBody(CtConstructor src, ClassMap map)
219: throws CannotCompileException {
220: setBody0(src.declaringClass, src.methodInfo, declaringClass,
221: methodInfo, map);
222: }
223:
224: /**
225: * Inserts bytecode just after another constructor in the super class
226: * or this class is called.
227: * It does not work if this object represents a class initializer.
228: *
229: * @param src the source code representing the inserted bytecode.
230: * It must be a single statement or block.
231: */
232: public void insertBeforeBody(String src)
233: throws CannotCompileException {
234: declaringClass.checkModify();
235: if (isClassInitializer())
236: throw new CannotCompileException("class initializer");
237:
238: CodeAttribute ca = methodInfo.getCodeAttribute();
239: CodeIterator iterator = ca.iterator();
240: Bytecode b = new Bytecode(methodInfo.getConstPool(), ca
241: .getMaxStack(), ca.getMaxLocals());
242: b.setStackDepth(ca.getMaxStack());
243: Javac jv = new Javac(b, declaringClass);
244: try {
245: jv.recordParams(getParameterTypes(), false);
246: jv.compileStmnt(src);
247: ca.setMaxStack(b.getMaxStack());
248: ca.setMaxLocals(b.getMaxLocals());
249: iterator.skipConstructor();
250: int pos = iterator.insertEx(b.get());
251: iterator.insert(b.getExceptionTable(), pos);
252: } catch (NotFoundException e) {
253: throw new CannotCompileException(e);
254: } catch (CompileError e) {
255: throw new CannotCompileException(e);
256: } catch (BadBytecode e) {
257: throw new CannotCompileException(e);
258: }
259: }
260:
261: /* This method is called by addCatch() in CtBehavior.
262: * super() and this() must not be in a try statement.
263: */
264: int getStartPosOfBody(CodeAttribute ca)
265: throws CannotCompileException {
266: CodeIterator ci = ca.iterator();
267: try {
268: ci.skipConstructor();
269: return ci.next();
270: } catch (BadBytecode e) {
271: throw new CannotCompileException(e);
272: }
273: }
274:
275: /**
276: * Makes a copy of this constructor and converts it into a method.
277: * The signature of the mehtod is the same as the that of this constructor.
278: * The return type is <code>void</code>. The resulting method must be
279: * appended to the class specified by <code>declaring</code>.
280: * If this constructor is a static initializer, the resulting method takes
281: * no parameter.
282: *
283: * <p>An occurrence of another constructor call <code>this()</code>
284: * or a super constructor call <code>super()</code> is
285: * eliminated from the resulting method.
286: *
287: * <p>The immediate super class of the class declaring this constructor
288: * must be also a super class of the class declaring the resulting method.
289: * If the constructor accesses a field, the class declaring the resulting method
290: * must also declare a field with the same name and type.
291: *
292: * @param name the name of the resulting method.
293: * @param declaring the class declaring the resulting method.
294: */
295: public CtMethod toMethod(String name, CtClass declaring)
296: throws CannotCompileException {
297: return toMethod(name, declaring, null);
298: }
299:
300: /**
301: * Makes a copy of this constructor and converts it into a method.
302: * The signature of the mehtod is the same as the that of this constructor.
303: * The return type is <code>void</code>. The resulting method must be
304: * appended to the class specified by <code>declaring</code>.
305: * If this constructor is a static initializer, the resulting method takes
306: * no parameter.
307: *
308: * <p>An occurrence of another constructor call <code>this()</code>
309: * or a super constructor call <code>super()</code> is
310: * eliminated from the resulting method.
311: *
312: * <p>The immediate super class of the class declaring this constructor
313: * must be also a super class of the class declaring the resulting method.
314: * If the constructor accesses a field, the class declaring the resulting method
315: * must also declare a field with the same name and type.
316: *
317: * @param name the name of the resulting method.
318: * @param declaring the class declaring the resulting method.
319: * @param map the hash table associating original class names
320: * with substituted names. The original class names will be
321: * replaced while making a copy.
322: * <code>map</code> can be <code>null</code>.
323: */
324: public CtMethod toMethod(String name, CtClass declaring,
325: ClassMap map) throws CannotCompileException {
326: CtMethod method = new CtMethod(null, declaring);
327: method.copy(this , false, map);
328: if (isConstructor()) {
329: MethodInfo minfo = method.getMethodInfo2();
330: CodeAttribute ca = minfo.getCodeAttribute();
331: if (ca != null)
332: removeConsCall(ca);
333: }
334:
335: method.setName(name);
336: return method;
337: }
338:
339: private static void removeConsCall(CodeAttribute ca)
340: throws CannotCompileException {
341: CodeIterator iterator = ca.iterator();
342: try {
343: int pos = iterator.skipConstructor();
344: if (pos >= 0) {
345: int mref = iterator.u16bitAt(pos + 1);
346: String desc = ca.getConstPool().getMethodrefType(mref);
347: int num = Descriptor.numOfParameters(desc) + 1;
348: if (num > 3)
349: iterator.insertGap(pos, num - 3);
350:
351: iterator.writeByte(Opcode.POP, pos++); // this
352: iterator.writeByte(Opcode.NOP, pos);
353: iterator.writeByte(Opcode.NOP, pos + 1);
354: Descriptor.Iterator it = new Descriptor.Iterator(desc);
355: while (true) {
356: it.next();
357: if (it.isParameter())
358: iterator.writeByte(it.is2byte() ? Opcode.POP2
359: : Opcode.POP, pos++);
360: else
361: break;
362: }
363: }
364: } catch (BadBytecode e) {
365: throw new CannotCompileException(e);
366: }
367: }
368: }
|