/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist;
import javassist.bytecode.*;
import javassist.compiler.Javac;
import javassist.compiler.CompileError;
import javassist.expr.ExprEditor;
/* Some methods do nothing except calling the super's method.
* They might seem redundant but they are necessary so that javadoc
* includes the description of those methods in the page of this class.
*/
/**
* An instance of CtConstructor represents a constructor.
* It may represent a static constructor
* (class initializer). To distinguish a constructor and a class
* initializer, call isClassInitializer()
.
*
* @see CtClass#getDeclaredConstructors()
* @see CtClass#getClassInitializer()
* @see CtNewConstructor
*/
public final class CtConstructor extends CtBehavior {
protected CtConstructor next;
protected CtConstructor(MethodInfo minfo, CtClass declaring) {
super(declaring, minfo);
next = null;
}
/**
* Creates a constructor with no constructor body.
* The created constructor
* must be added to a class with CtClass.addConstructor()
.
*
*
The created constructor does not include a constructor body,
* must be specified with All occurrences of class names in the created constructor
* are replaced with names specified by
* By default, all the occurrences of the names of the class
* declaring Note: if the All occurrences of the class names in the copied body
* are replaced with the names specified by
* Every setBody()
.
*
* @param declaring the class to which the created method is added.
* @param parameters a list of the parameter types
*
* @see CtClass#addConstructor(CtConstructor)
* @see CtConstructor#setBody(String)
* @see CtConstructor#setBody(CtConstructor,ClassMap)
*/
public CtConstructor(CtClass[] parameters, CtClass declaring) {
this((MethodInfo)null, declaring);
ConstPool cp = declaring.getClassFile2().getConstPool();
String desc = Descriptor.ofConstructor(parameters);
methodInfo = new MethodInfo(cp, "CtConstructor
object.
* The created constructor must be
* added to a class with CtClass.addConstructor()
.
*
* map
if map
is not null
.
*
* src
and the superclass are replaced
* with the name of the class and the superclass that
* the created constructor is added to.
* This is done whichever map
is null or not.
* To prevent this replacement, call ClassMap.fix()
.
*
* .class
notation (for example,
* String.class
) is included in an expression, the
* Javac compiler may produce a helper method.
* Since this constructor never
* copies this helper method, the programmers have the responsiblity of
* copying it. Otherwise, use Class.forName()
in the
* expression.
*
* @param src the source method.
* @param declaring the class to which the created method is added.
* @param map the hashtable associating original class names
* with substituted names.
* It can be null
.
*
* @see CtClass#addConstructor(CtConstructor)
* @see ClassMap#fix(String)
*/
public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map)
throws CannotCompileException
{
this((MethodInfo)null, declaring);
MethodInfo srcInfo = src.methodInfo;
CtClass srcClass = src.getDeclaringClass();
ConstPool cp = declaring.getClassFile2().getConstPool();
if (map == null)
map = new ClassMap();
map.put(srcClass.getName(), declaring.getName());
try {
boolean patch = false;
CtClass srcSuper = srcClass.getSuperclass();
String destSuperName = declaring.getSuperclass().getName();
if (srcSuper != null) {
String srcSuperName = srcSuper.getName();
if (!srcSuperName.equals(destSuperName))
if (srcSuperName.equals(CtClass.javaLangObject))
patch = true;
else
map.put(srcSuperName, destSuperName);
}
methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
if (patch)
methodInfo.setSuperclass(destSuperName);
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
static CtConstructor append(CtConstructor list, CtConstructor tail) {
tail.next = null;
if (list == null)
return tail;
else {
CtConstructor lst = list;
while (lst.next != null)
lst = lst.next;
lst.next = tail;
return list;
}
}
static int count(CtConstructor m) {
int n = 0;
while (m != null) {
++n;
m = m.next;
}
return n;
}
/**
* Returns the MethodInfo representing the constructor in the
* class file.
*/
public MethodInfo getMethodInfo() {
return super.getMethodInfo();
}
/**
* Returns true if this object represents a constructor.
*/
public boolean isConstructor() {
return methodInfo.isConstructor();
}
/**
* Returns true if this object represents a static initializer.
*/
public boolean isClassInitializer() {
return methodInfo.isStaticInitializer();
}
/**
* Obtains the encoded modifiers of the constructor.
*
* @return modifiers encoded with
* javassist.Modifier
.
* @see Modifier
*/
public int getModifiers() {
return super.getModifiers();
}
/**
* Sets the encoded modifiers of the constructor.
*
* @see Modifier
*/
public void setModifiers(int mod) {
super.setModifiers(mod);
}
/**
* Obtains the name of this constructor.
* It is the same as the simple name of the class declaring this
* constructor. If this object represents a class initializer,
* then this method returns "<clinit>"
.
*/
public String getName() {
if (methodInfo.isStaticInitializer())
return MethodInfo.nameClinit;
else
return declaringClass.getName();
}
/**
* Returns the class that declares this constructor.
*/
public CtClass getDeclaringClass() {
return super.getDeclaringClass();
}
/**
* Obtains parameter types of this constructor.
*/
public CtClass[] getParameterTypes() throws NotFoundException {
return super.getParameterTypes();
}
/**
* Returns the character string representing the parameter types.
* If two constructors have the same parameter types,
* getSignature()
returns the same string.
*/
public String getSignature() {
return super.getSignature();
}
/**
* Obtains exceptions that this constructor may throw.
*/
public CtClass[] getExceptionTypes() throws NotFoundException {
return super.getExceptionTypes();
}
/**
* Sets exceptions that this constructor may throw.
*/
public void setExceptionTypes(CtClass[] types)
throws NotFoundException
{
super.setExceptionTypes(types);
}
/**
* Returns true if the constructor is the default one.
*/
public boolean isEmpty() {
CodeAttribute ca = getMethodInfo2().getCodeAttribute();
if (ca == null)
return false; // native or abstract??
// they are not allowed, though.
ConstPool cp = ca.getConstPool();
CodeIterator it = ca.iterator();
try {
int pos, desc;
return it.byteAt(it.next()) == Opcode.ALOAD_0
&& it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL
&& (desc = cp.isConstructor(CtClass.javaLangObject,
it.u16bitAt(pos + 1))) != 0
&& cp.getUtf8Info(desc).equals("()V")
&& it.byteAt(it.next()) == Opcode.RETURN
&& !it.hasNext();
}
catch (BadBytecode e) {}
return false;
}
/**
* Sets a constructor body.
*
* @param src the source code representing the constructor body.
* It must be a single statement or block.
* If it is null
, the substituted
* constructor body does nothing except calling
* super()
.
*/
public void setBody(String src) throws CannotCompileException {
if (src == null)
if (isClassInitializer())
src = ";";
else
src = "super();";
super.setBody(src);
}
/**
* Copies a constructor body from another constructor.
*
* map
if map
is not null
.
*
* @param src the method that the body is copied from.
* @param map the hashtable associating original class names
* with substituted names.
* It can be null
.
*/
public void setBody(CtConstructor src, ClassMap map)
throws CannotCompileException
{
setBody0(src.declaringClass, src.methodInfo,
declaringClass, methodInfo, map);
}
/**
* Obtains an attribute with the given name.
* If that attribute is not found in the class file, this
* method returns null.
*
* @param name attribute name
*/
public byte[] getAttribute(String name) {
return super.getAttribute(name);
}
/**
* Adds an attribute. The attribute is saved in the class file.
*
* @param name attribute name
* @param data attribute value
*/
public void setAttribute(String name, byte[] data) {
super.setAttribute(name, data);
}
/**
* Declares to use $cflow
for this constructor.
* If $cflow
is used, the class files modified
* with Javassist requires a support class
* javassist.runtime.Cflow
at runtime
* (other Javassist classes are not required at runtime).
*
* $cflow
variable is given a unique name.
* For example, if the given name is "Point.paint"
,
* then the variable is indicated by $cflow(Point.paint)
.
*
* @param name $cflow
name. It can include
* alphabets, numbers, _
,
* $
, and .
(dot).
*
* @see javassist.runtime.Cflow
*/
public void useCflow(String name) throws CannotCompileException {
super.useCflow(name);
}
/**
* Modifies the constructor body.
*
* @param converter specifies how to modify.
*/
public void instrument(CodeConverter converter)
throws CannotCompileException
{
super.instrument(converter);
}
/**
* Modifies the constructor body.
*
* @param editor specifies how to modify.
*/
public void instrument(ExprEditor editor)
throws CannotCompileException
{
super.instrument(editor);
}
/**
* Inserts bytecode at the beginning of the constructor body.
* Since the bytecode is inserted before a constructor in the super
* class or this class is called, the bytecode is subject
* to constraints described
* in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
* For example, it cannot access instance members although it can access
* static members.
*
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
*/
public void insertBefore(String src) throws CannotCompileException {
super.insertBefore(src);
}
/**
* Inserts bytecode just after another constructor in the super class
* or this class is called.
* It does not work if this object represents a class initializer.
*
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
*/
public void insertBeforeBody(String src) throws CannotCompileException {
declaringClass.checkModify();
if (isClassInitializer())
throw new CannotCompileException("class initializer");
CodeAttribute ca = methodInfo.getCodeAttribute();
CodeIterator iterator = ca.iterator();
Bytecode b = new Bytecode(methodInfo.getConstPool(),
ca.getMaxStack(), ca.getMaxLocals());
b.setStackDepth(ca.getMaxStack());
Javac jv = new Javac(b, declaringClass);
try {
jv.recordParams(getParameterTypes(), false);
jv.compileStmnt(src);
ca.setMaxStack(b.getMaxStack());
ca.setMaxLocals(b.getMaxLocals());
iterator.skipConstructor();
int pos = iterator.insertEx(b.get());
iterator.insert(b.getExceptionTable(), pos);
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
catch (CompileError e) {
throw new CannotCompileException(e);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
/**
* Inserts bytecode at the end of the constructor body.
* The bytecode is inserted just before every return insturction.
* It is not executed when an exception is thrown.
*
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
*/
public void insertAfter(String src)
throws CannotCompileException
{
super.insertAfter(src);
}
/**
* Inserts bytecode at the end of the constructor body.
* The bytecode is inserted just before every return insturction.
*
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
* @param asFinally true if the inserted bytecode is executed
* not only when the transfer normally returns
* but also when an exception is thrown.
*/
public void insertAfter(String src, boolean asFinally)
throws CannotCompileException
{
super.insertAfter(src, asFinally);
}
/**
* Adds a catch clause that handles an exception thrown in the
* constructor body.
* The catch clause must end with a return or throw statement.
*
* @param src the source code representing the catch clause.
* It must be a single statement or block.
* @param exceptionType the type of the exception handled by the
* catch clause.
*/
public void addCatch(String src, CtClass exceptionType)
throws CannotCompileException
{
super.addCatch(src, exceptionType);
}
}