xapi.bytecode.CtConstructor Maven / Gradle / Ivy
Show all versions of xapi-dev Show documentation
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- 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,
* or the Apache License Version 2.0.
*
* 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.
*
* MODIFIED BY James Nelson of We The Internet, 2013.
* Repackaged to avoid conflicts with different versions of Javassist,
* and modified Javassist APIs to make them more accessible to outside code.
*/
package xapi.bytecode;
import xapi.bytecode.api.Opcode;
import xapi.bytecode.attributes.CodeAttribute;
import xapi.source.X_Modifier;
public final class CtConstructor extends CtBehavior {
protected CtConstructor(MethodInfo minfo, CtClass declaring) {
super(declaring, minfo);
}
/**
* 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,
* which must be specified with 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, "", desc);
setModifiers(X_Modifier.PUBLIC);
}
/**
* Creates a copy of a CtConstructor
object.
* The created constructor must be
* added to a class with CtClass.addConstructor()
.
*
* All occurrences of class names in the created constructor
* are replaced with names specified by
* map
if map
is not null
.
*
*
By default, all the occurrences of the names of the class
* declaring 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()
* or put()
to explicitly specify replacement.
*
*
Note: if the .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);
copy(src, true, map);
}
/**
* 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();
}
/**
* Returns the constructor name followed by parameter types
* such as javassist.CtConstructor(CtClass[],CtClass)
.
*
* @since 3.5
*/
@Override
public String getLongName() {
return getDeclaringClass().getName()
+ (isConstructor() ? Descriptor.toString(getSignature())
: ("." + MethodInfo.nameClinit + "()"));
}
/**
* 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>"
.
*/
@Override
public String getName() {
if (methodInfo.isStaticInitializer()) {
return MethodInfo.nameClinit;
} else {
return declaringClass.getSimpleName();
}
}
/**
* Returns true if the constructor (or static initializer)
* is the default one. This method returns true if the constructor
* takes some arguments but it does not perform anything except
* calling super()
(the no-argument constructor of
* the super class).
*/
@Override
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;
int op0 = it.byteAt(it.next());
return op0 == Opcode.RETURN // empty static initializer
|| (op0 == Opcode.ALOAD_0
&& it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL
&& (desc = cp.isConstructor(getSuperclassName(),
it.u16bitAt(pos + 1))) != 0
&& "()V".equals(cp.getUtf8Info(desc))
&& it.byteAt(it.next()) == Opcode.RETURN
&& !it.hasNext());
}
catch (BadBytecode e) {}
return false;
}
private String getSuperclassName() {
ClassFile cf = declaringClass.getClassFile2();
return cf.getSuperclass();
}
/**
* Returns true if this constructor calls a constructor
* of the super class. This method returns false if it
* calls another constructor of this class by this()
.
*/
public boolean callsSuper() throws CannotCompileException {
CodeAttribute codeAttr = methodInfo.getCodeAttribute();
if (codeAttr != null) {
CodeIterator it = codeAttr.iterator();
try {
int index = it.skipSuperConstructor();
return index >= 0;
}
catch (BadBytecode e) {
throw new CannotCompileException(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()
.
* @throws CannotCompileException
*/
public void setBody(String src) throws CannotCompileException {
if (src == null)
{
if (isClassInitializer()) {
src = ";";
}
else {
src = "super();";
// TODO modify Ct classes for source gen instead of bytecode gen
// super.setBody(src);
}
}
}
/**
* Copies a constructor body from another constructor.
*
*
All occurrences of the class names in the copied body
* are replaced with the names specified by
* 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);
}
// /**
// * 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 {
// CtClass cc = declaringClass;
// cc.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, cc);
// 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);
// methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
// }
// catch (NotFoundException e) {
// throw new CannotCompileException(e);
// }
// catch (CompileError e) {
// throw new CannotCompileException(e);
// }
// catch (BadBytecode e) {
// throw new CannotCompileException(e);
// }
// }
/* This method is called by addCatch() in CtBehavior.
* super() and this() must not be in a try statement.
*/
@Override
int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
CodeIterator ci = ca.iterator();
try {
ci.skipConstructor();
return ci.next();
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
/**
* Makes a copy of this constructor and converts it into a method.
* The signature of the mehtod is the same as the that of this constructor.
* The return type is void
. The resulting method must be
* appended to the class specified by declaring
.
* If this constructor is a static initializer, the resulting method takes
* no parameter.
*
*
An occurrence of another constructor call this()
* or a super constructor call super()
is
* eliminated from the resulting method.
*
*
The immediate super class of the class declaring this constructor
* must be also a super class of the class declaring the resulting method.
* If the constructor accesses a field, the class declaring the resulting method
* must also declare a field with the same name and type.
*
* @param name the name of the resulting method.
* @param declaring the class declaring the resulting method.
*/
public CtMethod toMethod(String name, CtClass declaring)
throws CannotCompileException
{
return toMethod(name, declaring, null);
}
/**
* Makes a copy of this constructor and converts it into a method.
* The signature of the method is the same as the that of this constructor.
* The return type is void
. The resulting method must be
* appended to the class specified by declaring
.
* If this constructor is a static initializer, the resulting method takes
* no parameter.
*
*
An occurrence of another constructor call this()
* or a super constructor call super()
is
* eliminated from the resulting method.
*
*
The immediate super class of the class declaring this constructor
* must be also a super class of the class declaring the resulting method
* (this is obviously true if the second parameter declaring
is
* the same as the class declaring this constructor).
* If the constructor accesses a field, the class declaring the resulting method
* must also declare a field with the same name and type.
*
* @param name the name of the resulting method.
* @param declaring the class declaring the resulting method.
* It is normally the same as the class declaring this
* constructor.
* @param map the hash table associating original class names
* with substituted names. The original class names will be
* replaced while making a copy.
* map
can be null
.
*/
public CtMethod toMethod(String name, CtClass declaring, ClassMap map)
throws CannotCompileException
{
CtMethod method = new CtMethod(null, declaring);
method.copy(this, false, map);
if (isConstructor()) {
MethodInfo minfo = method.getMethodInfo2();
CodeAttribute ca = minfo.getCodeAttribute();
if (ca != null) {
removeConsCall(ca);
// try {
// methodInfo.rebuildStackMapIf6(declaring.getClassPool(),
// declaring.getClassFile2());
// }
// catch (BadBytecode e) {
// throw new CannotCompileException(e);
// }
}
}
method.setName(name);
return method;
}
private static void removeConsCall(CodeAttribute ca)
throws CannotCompileException
{
CodeIterator iterator = ca.iterator();
try {
int pos = iterator.skipConstructor();
if (pos >= 0) {
int mref = iterator.u16bitAt(pos + 1);
String desc = ca.getConstPool().getMethodrefType(mref);
int num = Descriptor.numOfParameters(desc) + 1;
if (num > 3) {
pos = iterator.insertGapAt(pos, num - 3, false).position;
}
iterator.writeByte(Opcode.POP, pos++); // this
iterator.writeByte(Opcode.NOP, pos);
iterator.writeByte(Opcode.NOP, pos + 1);
Descriptor.Iterator it = new Descriptor.Iterator(desc);
while (true) {
it.next();
if (it.isParameter()) {
iterator.writeByte(it.is2byte() ? Opcode.POP2 : Opcode.POP,
pos++);
} else {
break;
}
}
}
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
}