/*
* 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.
*/
package javassist;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.AttributeInfo;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.Bytecode;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import javassist.bytecode.ExceptionsAttribute;
import javassist.bytecode.LineNumberAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.LocalVariableTypeAttribute;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.ParameterAnnotationsAttribute;
import javassist.bytecode.SignatureAttribute;
import javassist.bytecode.StackMap;
import javassist.bytecode.StackMapTable;
import javassist.compiler.CompileError;
import javassist.compiler.Javac;
import javassist.expr.ExprEditor;
CtBehavior
represents a method, a constructor,
or a static constructor (class initializer).
It is the abstract super class of
CtMethod
and CtConstructor
.
To directly read or modify bytecode, obtain MethodInfo
objects.
See Also: - getMethodInfo()
/**
* <code>CtBehavior</code> represents a method, a constructor,
* or a static constructor (class initializer).
* It is the abstract super class of
* <code>CtMethod</code> and <code>CtConstructor</code>.
*
* <p>To directly read or modify bytecode, obtain <code>MethodInfo</code>
* objects.
*
* @see #getMethodInfo()
*/
public abstract class CtBehavior extends CtMember {
protected MethodInfo methodInfo;
protected CtBehavior(CtClass clazz, MethodInfo minfo) {
super(clazz);
methodInfo = minfo;
}
Params: - isCons – true if this is a constructor.
/**
* @param isCons true if this is a constructor.
*/
void copy(CtBehavior src, boolean isCons, ClassMap map)
throws CannotCompileException
{
CtClass declaring = declaringClass;
MethodInfo srcInfo = src.methodInfo;
CtClass srcClass = src.getDeclaringClass();
ConstPool cp = declaring.getClassFile2().getConstPool();
map = new ClassMap(map);
map.put(srcClass.getName(), declaring.getName());
try {
boolean patch = false;
CtClass srcSuper = srcClass.getSuperclass();
CtClass destSuper = declaring.getSuperclass();
String destSuperName = null;
if (srcSuper != null && destSuper != null) {
String srcSuperName = srcSuper.getName();
destSuperName = destSuper.getName();
if (!srcSuperName.equals(destSuperName))
if (srcSuperName.equals(CtClass.javaLangObject))
patch = true;
else
map.putIfNone(srcSuperName, destSuperName);
}
// a stack map table is copied from srcInfo.
methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
if (isCons && patch)
methodInfo.setSuperclass(destSuperName);
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
@Override
protected void extendToString(StringBuffer buffer) {
buffer.append(' ');
buffer.append(getName());
buffer.append(' ');
buffer.append(methodInfo.getDescriptor());
}
Returns the method or constructor name followed by parameter types
such as javassist.CtBehavior.stBody(String)
.
Since: 3.5
/**
* Returns the method or constructor name followed by parameter types
* such as <code>javassist.CtBehavior.stBody(String)</code>.
*
* @since 3.5
*/
public abstract String getLongName();
Returns the MethodInfo
representing this method/constructor in the
class file.
If you modify the bytecode through the returned
MethodInfo
object, you might have to explicitly
rebuild a stack map table. Javassist does not automatically
rebuild it for avoiding unnecessary rebuilding.
See Also: - rebuildStackMap.rebuildStackMap(ClassPool)
/**
* Returns the <code>MethodInfo</code> representing this method/constructor in the
* class file.
*
* <p>If you modify the bytecode through the returned
* <code>MethodInfo</code> object, you might have to explicitly
* rebuild a stack map table. Javassist does not automatically
* rebuild it for avoiding unnecessary rebuilding.
*
* @see javassist.bytecode.MethodInfo#rebuildStackMap(ClassPool)
*/
public MethodInfo getMethodInfo() {
declaringClass.checkModify();
return methodInfo;
}
Returns the MethodInfo
representing the method/constructor in the
class file (read only).
Normal applications do not need calling this method. Use
getMethodInfo()
.
The MethodInfo
object obtained by this method
is read only. Changes to this object might not be reflected
on a class file generated by toBytecode()
,
toClass()
, etc in CtClass
.
This method is available even if the CtClass
containing this method is frozen. However, if the class is
frozen, the MethodInfo
might be also pruned.
See Also: - getMethodInfo()
- CtClass.isFrozen()
- CtClass.prune()
/**
* Returns the <code>MethodInfo</code> representing the method/constructor in the
* class file (read only).
* Normal applications do not need calling this method. Use
* <code>getMethodInfo()</code>.
*
* <p>The <code>MethodInfo</code> object obtained by this method
* is read only. Changes to this object might not be reflected
* on a class file generated by <code>toBytecode()</code>,
* <code>toClass()</code>, etc in <code>CtClass</code>.
*
* <p>This method is available even if the <code>CtClass</code>
* containing this method is frozen. However, if the class is
* frozen, the <code>MethodInfo</code> might be also pruned.
*
* @see #getMethodInfo()
* @see CtClass#isFrozen()
* @see CtClass#prune()
*/
public MethodInfo getMethodInfo2() { return methodInfo; }
Obtains the modifiers of the method/constructor.
See Also: Returns: modifiers encoded with
javassist.Modifier
.
/**
* Obtains the modifiers of the method/constructor.
*
* @return modifiers encoded with
* <code>javassist.Modifier</code>.
* @see Modifier
*/
@Override
public int getModifiers() {
return AccessFlag.toModifier(methodInfo.getAccessFlags());
}
Sets the encoded modifiers of the method/constructor.
Changing the modifiers may cause a problem.
For example, if a non-static method is changed to static,
the method will be rejected by the bytecode verifier.
See Also: - Modifier
/**
* Sets the encoded modifiers of the method/constructor.
*
* <p>Changing the modifiers may cause a problem.
* For example, if a non-static method is changed to static,
* the method will be rejected by the bytecode verifier.
*
* @see Modifier
*/
@Override
public void setModifiers(int mod) {
declaringClass.checkModify();
methodInfo.setAccessFlags(AccessFlag.of(mod));
}
Returns true if the class has the specified annotation type.
Params: - typeName – the name of annotation type.
Returns: true
if the annotation is found,
otherwise false
.Since: 3.21
/**
* Returns true if the class has the specified annotation type.
*
* @param typeName the name of annotation type.
* @return <code>true</code> if the annotation is found,
* otherwise <code>false</code>.
* @since 3.21
*/
@Override
public boolean hasAnnotation(String typeName) {
MethodInfo mi = getMethodInfo2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.invisibleTag);
AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.visibleTag);
return CtClassType.hasAnnotationType(typeName,
getDeclaringClass().getClassPool(),
ainfo, ainfo2);
}
Returns the annotation if the class has the specified annotation class.
For example, if an annotation @Author
is associated
with this method/constructor, an Author
object is returned.
The member values can be obtained by calling methods on
the Author
object.
Params: - clz – the annotation class.
Returns: the annotation if found, otherwise null
. Since: 3.11
/**
* Returns the annotation if the class has the specified annotation class.
* For example, if an annotation <code>@Author</code> is associated
* with this method/constructor, an <code>Author</code> object is returned.
* The member values can be obtained by calling methods on
* the <code>Author</code> object.
*
* @param clz the annotation class.
* @return the annotation if found, otherwise <code>null</code>.
* @since 3.11
*/
@Override
public Object getAnnotation(Class<?> clz) throws ClassNotFoundException {
MethodInfo mi = getMethodInfo2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.invisibleTag);
AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.visibleTag);
return CtClassType.getAnnotationType(clz,
getDeclaringClass().getClassPool(),
ainfo, ainfo2);
}
Returns the annotations associated with this method or constructor.
See Also: Returns: an array of annotation-type objects. Since: 3.1
/**
* Returns the annotations associated with this method or constructor.
*
* @return an array of annotation-type objects.
* @see #getAvailableAnnotations()
* @since 3.1
*/
@Override
public Object[] getAnnotations() throws ClassNotFoundException {
return getAnnotations(false);
}
Returns the annotations associated with this method or constructor.
If any annotations are not on the classpath, they are not included
in the returned array.
See Also: Returns: an array of annotation-type objects. Since: 3.3
/**
* Returns the annotations associated with this method or constructor.
* If any annotations are not on the classpath, they are not included
* in the returned array.
*
* @return an array of annotation-type objects.
* @see #getAnnotations()
* @since 3.3
*/
@Override
public Object[] getAvailableAnnotations(){
try{
return getAnnotations(true);
}
catch (ClassNotFoundException e){
throw new RuntimeException("Unexpected exception", e);
}
}
private Object[] getAnnotations(boolean ignoreNotFound)
throws ClassNotFoundException
{
MethodInfo mi = getMethodInfo2();
AnnotationsAttribute ainfo = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.invisibleTag);
AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
mi.getAttribute(AnnotationsAttribute.visibleTag);
return CtClassType.toAnnotationType(ignoreNotFound,
getDeclaringClass().getClassPool(),
ainfo, ainfo2);
}
Returns the parameter annotations associated with this method or constructor.
See Also: Returns: an array of annotation-type objects. The length of the returned array is
equal to the number of the formal parameters. If each parameter has no
annotation, the elements of the returned array are empty arrays. Since: 3.1
/**
* Returns the parameter annotations associated with this method or constructor.
*
* @return an array of annotation-type objects. The length of the returned array is
* equal to the number of the formal parameters. If each parameter has no
* annotation, the elements of the returned array are empty arrays.
*
* @see #getAvailableParameterAnnotations()
* @see #getAnnotations()
* @since 3.1
*/
public Object[][] getParameterAnnotations() throws ClassNotFoundException {
return getParameterAnnotations(false);
}
Returns the parameter annotations associated with this method or constructor.
If any annotations are not on the classpath, they are not included in the
returned array.
See Also: Returns: an array of annotation-type objects. The length of the returned array is
equal to the number of the formal parameters. If each parameter has no
annotation, the elements of the returned array are empty arrays. Since: 3.3
/**
* Returns the parameter annotations associated with this method or constructor.
* If any annotations are not on the classpath, they are not included in the
* returned array.
*
* @return an array of annotation-type objects. The length of the returned array is
* equal to the number of the formal parameters. If each parameter has no
* annotation, the elements of the returned array are empty arrays.
*
* @see #getParameterAnnotations()
* @see #getAvailableAnnotations()
* @since 3.3
*/
public Object[][] getAvailableParameterAnnotations(){
try {
return getParameterAnnotations(true);
}
catch(ClassNotFoundException e) {
throw new RuntimeException("Unexpected exception", e);
}
}
Object[][] getParameterAnnotations(boolean ignoreNotFound)
throws ClassNotFoundException
{
MethodInfo mi = getMethodInfo2();
ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute)
mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag);
ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute)
mi.getAttribute(ParameterAnnotationsAttribute.visibleTag);
return CtClassType.toAnnotationType(ignoreNotFound,
getDeclaringClass().getClassPool(),
ainfo, ainfo2, mi);
}
Obtains parameter types of this method/constructor.
/**
* Obtains parameter types of this method/constructor.
*/
public CtClass[] getParameterTypes() throws NotFoundException {
return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
declaringClass.getClassPool());
}
Obtains the type of the returned value.
/**
* Obtains the type of the returned value.
*/
CtClass getReturnType0() throws NotFoundException {
return Descriptor.getReturnType(methodInfo.getDescriptor(),
declaringClass.getClassPool());
}
Returns the method signature (the parameter types
and the return type).
The method signature is represented by a character string
called method descriptor, which is defined in the JVM specification.
If two methods/constructors have
the same parameter types
and the return type, getSignature()
returns the
same string (the return type of constructors is void
).
Note that the returned string is not the type signature
contained in the SignatureAttirbute
. It is
a descriptor.
See Also: - Descriptor
- getGenericSignature()
/**
* Returns the method signature (the parameter types
* and the return type).
* The method signature is represented by a character string
* called method descriptor, which is defined in the JVM specification.
* If two methods/constructors have
* the same parameter types
* and the return type, <code>getSignature()</code> returns the
* same string (the return type of constructors is <code>void</code>).
*
* <p>Note that the returned string is not the type signature
* contained in the <code>SignatureAttirbute</code>. It is
* a descriptor.
*
* @see javassist.bytecode.Descriptor
* @see #getGenericSignature()
*/
@Override
public String getSignature() {
return methodInfo.getDescriptor();
}
Returns the generic signature of the method.
It represents parameter types including type variables.
See Also: - toMethodSignature.toMethodSignature(String)
Since: 3.17
/**
* Returns the generic signature of the method.
* It represents parameter types including type variables.
*
* @see SignatureAttribute#toMethodSignature(String)
* @since 3.17
*/
@Override
public String getGenericSignature() {
SignatureAttribute sa
= (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag);
return sa == null ? null : sa.getSignature();
}
Set the generic signature of the method. It represents parameter types including type variables. See CtClass.setGenericSignature(String)
for a code sample. Params: - sig – a new generic signature.
See Also: Since: 3.17
/**
* Set the generic signature of the method.
* It represents parameter types including type variables.
* See {@link javassist.CtClass#setGenericSignature(String)}
* for a code sample.
*
* @param sig a new generic signature.
* @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
* @since 3.17
*/
@Override
public void setGenericSignature(String sig) {
declaringClass.checkModify();
methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig));
}
Obtains exceptions that this method/constructor may throw.
Returns: a zero-length array if there is no throws clause.
/**
* Obtains exceptions that this method/constructor may throw.
*
* @return a zero-length array if there is no throws clause.
*/
public CtClass[] getExceptionTypes() throws NotFoundException {
String[] exceptions;
ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
if (ea == null)
exceptions = null;
else
exceptions = ea.getExceptions();
return declaringClass.getClassPool().get(exceptions);
}
Sets exceptions that this method/constructor may throw.
/**
* Sets exceptions that this method/constructor may throw.
*/
public void setExceptionTypes(CtClass[] types) throws NotFoundException {
declaringClass.checkModify();
if (types == null || types.length == 0) {
methodInfo.removeExceptionsAttribute();
return;
}
String[] names = new String[types.length];
for (int i = 0; i < types.length; ++i)
names[i] = types[i].getName();
ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
if (ea == null) {
ea = new ExceptionsAttribute(methodInfo.getConstPool());
methodInfo.setExceptionsAttribute(ea);
}
ea.setExceptions(names);
}
Returns true if the body is empty.
/**
* Returns true if the body is empty.
*/
public abstract boolean isEmpty();
Sets a method/constructor body.
Params: - src – the source code representing the body.
It must be a single statement or block.
If it is
null
, the substituted
body does nothing except returning zero or null.
/**
* Sets a method/constructor body.
*
* @param src the source code representing the body.
* It must be a single statement or block.
* If it is <code>null</code>, the substituted
* body does nothing except returning zero or null.
*/
public void setBody(String src) throws CannotCompileException {
setBody(src, null, null);
}
Sets a method/constructor body.
Params: - src – the source code representing the body.
It must be a single statement or block.
If it is
null
, the substituted
body does nothing except returning zero or null. - delegateObj – the source text specifying the object
that is called on by
$proceed()
. - delegateMethod – the name of the method
that is called by
$proceed()
.
/**
* Sets a method/constructor body.
*
* @param src the source code representing the body.
* It must be a single statement or block.
* If it is <code>null</code>, the substituted
* body does nothing except returning zero or null.
* @param delegateObj the source text specifying the object
* that is called on by <code>$proceed()</code>.
* @param delegateMethod the name of the method
* that is called by <code>$proceed()</code>.
*/
public void setBody(String src,
String delegateObj, String delegateMethod)
throws CannotCompileException
{
CtClass cc = declaringClass;
cc.checkModify();
try {
Javac jv = new Javac(cc);
if (delegateMethod != null)
jv.recordProceed(delegateObj, delegateMethod);
Bytecode b = jv.compileBody(this, src);
methodInfo.setCodeAttribute(b.toCodeAttribute());
methodInfo.setAccessFlags(methodInfo.getAccessFlags()
& ~AccessFlag.ABSTRACT);
methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
declaringClass.rebuildClassFile();
}
catch (CompileError e) {
throw new CannotCompileException(e);
} catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
static void setBody0(CtClass srcClass, MethodInfo srcInfo,
CtClass destClass, MethodInfo destInfo,
ClassMap map)
throws CannotCompileException
{
destClass.checkModify();
map = new ClassMap(map);
map.put(srcClass.getName(), destClass.getName());
try {
CodeAttribute cattr = srcInfo.getCodeAttribute();
if (cattr != null) {
ConstPool cp = destInfo.getConstPool();
CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map);
destInfo.setCodeAttribute(ca);
// a stack map table is copied to destInfo.
}
}
catch (CodeAttribute.RuntimeCopyException e) {
/* the exception may be thrown by copy() in CodeAttribute.
*/
throw new CannotCompileException(e);
}
destInfo.setAccessFlags(destInfo.getAccessFlags()
& ~AccessFlag.ABSTRACT);
destClass.rebuildClassFile();
}
Obtains an attribute with the given name.
If that attribute is not found in the class file, this
method returns null.
Note that an attribute is a data block specified by the class file format. It is not an annotation. See AttributeInfo
.
Params: - name – attribute name
/**
* Obtains an attribute with the given name.
* If that attribute is not found in the class file, this
* method returns null.
*
* <p>Note that an attribute is a data block specified by
* the class file format. It is not an annotation.
* See {@link javassist.bytecode.AttributeInfo}.
*
* @param name attribute name
*/
@Override
public byte[] getAttribute(String name)
{
AttributeInfo ai = methodInfo.getAttribute(name);
if (ai == null)
return null;
return ai.get();
}
Adds an attribute. The attribute is saved in the class file.
Note that an attribute is a data block specified by the class file format. It is not an annotation. See AttributeInfo
.
Params: - name – attribute name
- data – attribute value
/**
* Adds an attribute. The attribute is saved in the class file.
*
* <p>Note that an attribute is a data block specified by
* the class file format. It is not an annotation.
* See {@link javassist.bytecode.AttributeInfo}.
*
* @param name attribute name
* @param data attribute value
*/
@Override
public void setAttribute(String name, byte[] data)
{
declaringClass.checkModify();
methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
name, data));
}
Declares to use $cflow
for this method/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).
Every $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)
.
Params: - name –
$cflow
name. It can include
alphabets, numbers, _
,
$
, and .
(dot).
See Also:
/**
* Declares to use <code>$cflow</code> for this method/constructor.
* If <code>$cflow</code> is used, the class files modified
* with Javassist requires a support class
* <code>javassist.runtime.Cflow</code> at runtime
* (other Javassist classes are not required at runtime).
*
* <p>Every <code>$cflow</code> variable is given a unique name.
* For example, if the given name is <code>"Point.paint"</code>,
* then the variable is indicated by <code>$cflow(Point.paint)</code>.
*
* @param name <code>$cflow</code> name. It can include
* alphabets, numbers, <code>_</code>,
* <code>$</code>, and <code>.</code> (dot).
*
* @see javassist.runtime.Cflow
*/
public void useCflow(String name) throws CannotCompileException
{
CtClass cc = declaringClass;
cc.checkModify();
ClassPool pool = cc.getClassPool();
String fname;
int i = 0;
while (true) {
fname = "_cflow$" + i++;
try {
cc.getDeclaredField(fname);
}
catch(NotFoundException e) {
break;
}
}
pool.recordCflow(name, declaringClass.getName(), fname);
try {
CtClass type = pool.get("javassist.runtime.Cflow");
CtField field = new CtField(type, fname, cc);
field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
cc.addField(field, CtField.Initializer.byNew(type));
insertBefore(fname + ".enter();", false);
String src = fname + ".exit();";
insertAfter(src, true);
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
}
Declares a new local variable. The scope of this variable is the
whole method body. The initial value of that variable is not set.
The declared variable can be accessed in the code snippet inserted
by insertBefore()
, insertAfter()
, etc.
If the second parameter asFinally
to
insertAfter()
is true, the declared local variable
is not visible from the code inserted by insertAfter()
.
Params: - name – the name of the variable
- type – the type of the variable
See Also:
/**
* Declares a new local variable. The scope of this variable is the
* whole method body. The initial value of that variable is not set.
* The declared variable can be accessed in the code snippet inserted
* by <code>insertBefore()</code>, <code>insertAfter()</code>, etc.
*
* <p>If the second parameter <code>asFinally</code> to
* <code>insertAfter()</code> is true, the declared local variable
* is not visible from the code inserted by <code>insertAfter()</code>.
*
* @param name the name of the variable
* @param type the type of the variable
* @see #insertBefore(String)
* @see #insertAfter(String)
*/
public void addLocalVariable(String name, CtClass type)
throws CannotCompileException
{
declaringClass.checkModify();
ConstPool cp = methodInfo.getConstPool();
CodeAttribute ca = methodInfo.getCodeAttribute();
if (ca == null)
throw new CannotCompileException("no method body");
LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute(
LocalVariableAttribute.tag);
if (va == null) {
va = new LocalVariableAttribute(cp);
ca.getAttributes().add(va);
}
int maxLocals = ca.getMaxLocals();
String desc = Descriptor.of(type);
va.addEntry(0, ca.getCodeLength(),
cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals);
ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc));
}
Inserts a new parameter, which becomes the first parameter.
/**
* Inserts a new parameter, which becomes the first parameter.
*/
public void insertParameter(CtClass type)
throws CannotCompileException
{
declaringClass.checkModify();
String desc = methodInfo.getDescriptor();
String desc2 = Descriptor.insertParameter(type, desc);
try {
addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
methodInfo.setDescriptor(desc2);
}
Appends a new parameter, which becomes the last parameter.
/**
* Appends a new parameter, which becomes the last parameter.
*/
public void addParameter(CtClass type)
throws CannotCompileException
{
declaringClass.checkModify();
String desc = methodInfo.getDescriptor();
String desc2 = Descriptor.appendParameter(type, desc);
int offset = Modifier.isStatic(getModifiers()) ? 0 : 1;
try {
addParameter2(offset + Descriptor.paramSize(desc), type, desc);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
methodInfo.setDescriptor(desc2);
}
private void addParameter2(int where, CtClass type, String desc)
throws BadBytecode
{
CodeAttribute ca = methodInfo.getCodeAttribute();
if (ca != null) {
int size = 1;
char typeDesc = 'L';
int classInfo = 0;
if (type.isPrimitive()) {
CtPrimitiveType cpt = (CtPrimitiveType)type;
size = cpt.getDataSize();
typeDesc = cpt.getDescriptor();
}
else
classInfo = methodInfo.getConstPool().addClassInfo(type);
ca.insertLocalVar(where, size);
LocalVariableAttribute va
= (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag);
if (va != null)
va.shiftIndex(where, size);
LocalVariableTypeAttribute lvta
= (LocalVariableTypeAttribute)ca.getAttribute(LocalVariableTypeAttribute.tag);
if (lvta != null)
lvta.shiftIndex(where, size);
StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
if (smt != null)
smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
StackMap sm = (StackMap)ca.getAttribute(StackMap.tag);
if (sm != null)
sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
}
}
Modifies the method/constructor body.
Params: - converter – specifies how to modify.
/**
* Modifies the method/constructor body.
*
* @param converter specifies how to modify.
*/
public void instrument(CodeConverter converter)
throws CannotCompileException
{
declaringClass.checkModify();
ConstPool cp = methodInfo.getConstPool();
converter.doit(getDeclaringClass(), methodInfo, cp);
}
Modifies the method/constructor body.
While executing this method, only replace()
in Expr
is available for bytecode modification.
Other methods such as insertBefore()
may collapse
the bytecode because the ExprEditor
loses
its current position.
Params: - editor – specifies how to modify.
See Also:
/**
* Modifies the method/constructor body.
*
* <p>While executing this method, only <code>replace()</code>
* in <code>Expr</code> is available for bytecode modification.
* Other methods such as <code>insertBefore()</code> may collapse
* the bytecode because the <code>ExprEditor</code> loses
* its current position.
*
* @param editor specifies how to modify.
* @see javassist.expr.Expr#replace(String)
* @see #insertBefore(String)
*/
public void instrument(ExprEditor editor)
throws CannotCompileException
{
// if the class is not frozen,
// does not turn the modified flag on.
if (declaringClass.isFrozen())
declaringClass.checkModify();
if (editor.doit(declaringClass, methodInfo))
declaringClass.checkModify();
}
Inserts bytecode at the beginning of the body.
If this object represents a constructor,
the bytecode is inserted before
a constructor in the super class or this class is called.
Therefore, the inserted 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 fields or methods although
it may assign a value to an instance field directly declared in this
class. Accessing static fields and methods is allowed.
Use insertBeforeBody()
in CtConstructor
.
Params: - src – the source code representing the inserted bytecode.
It must be a single statement or block.
See Also:
/**
* Inserts bytecode at the beginning of the body.
*
* <p>If this object represents a constructor,
* the bytecode is inserted before
* a constructor in the super class or this class is called.
* Therefore, the inserted 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 fields or methods although
* it may assign a value to an instance field directly declared in this
* class. Accessing static fields and methods is allowed.
* Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>.
*
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
* @see CtConstructor#insertBeforeBody(String)
*/
public void insertBefore(String src) throws CannotCompileException {
insertBefore(src, true);
}
private void insertBefore(String src, boolean rebuild)
throws CannotCompileException
{
CtClass cc = declaringClass;
cc.checkModify();
CodeAttribute ca = methodInfo.getCodeAttribute();
if (ca == null)
throw new CannotCompileException("no method body");
CodeIterator iterator = ca.iterator();
Javac jv = new Javac(cc);
try {
int nvars = jv.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
jv.recordParamNames(ca, nvars);
jv.recordLocalVariables(ca, 0);
jv.recordReturnType(getReturnType0(), false);
jv.compileStmnt(src);
Bytecode b = jv.getBytecode();
int stack = b.getMaxStack();
int locals = b.getMaxLocals();
if (stack > ca.getMaxStack())
ca.setMaxStack(stack);
if (locals > ca.getMaxLocals())
ca.setMaxLocals(locals);
int pos = iterator.insertEx(b.get());
iterator.insert(b.getExceptionTable(), pos);
if (rebuild)
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);
}
}
Inserts bytecode at the end of the body.
The bytecode is inserted just before every return instruction.
It is not executed when an exception is thrown.
Params: - src – the source code representing the inserted bytecode.
It must be a single statement or block.
/**
* Inserts bytecode at the end of the body.
* The bytecode is inserted just before every return instruction.
* 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
{
insertAfter(src, false, false);
}
Inserts bytecode at the end of the body.
The bytecode is inserted just before every return instruction.
Params: - src – the source code representing the inserted bytecode.
It must be a single statement or block.
- asFinally – true if the inserted bytecode is executed
not only when the control normally returns
but also when an exception is thrown.
If this parameter is true, the inserted code cannot
access local variables.
/**
* Inserts bytecode at the end of the body.
* The bytecode is inserted just before every return instruction.
*
* @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 control normally returns
* but also when an exception is thrown.
* If this parameter is true, the inserted code cannot
* access local variables.
*/
public void insertAfter(String src, boolean asFinally)
throws CannotCompileException
{
insertAfter(src, asFinally, false);
}
Inserts bytecode at the end of the body.
The bytecode is inserted just before every return instruction.
Params: - src – the source code representing the inserted bytecode.
It must be a single statement or block.
- asFinally – true if the inserted bytecode is executed
not only when the control normally returns
but also when an exception is thrown.
If this parameter is true, the inserted code cannot
access local variables.
- redundant – if true, redundant bytecode will be generated.
the redundancy is necessary when some compilers (Kotlin?)
generate the original bytecode.
The other
insertAfter
methods calls this method
with false
for this parameter.
A tip is to pass this.getDeclaringClass().isKotlin()
to this parameter.
See Also: Since: 3.26
/**
* Inserts bytecode at the end of the body.
* The bytecode is inserted just before every return instruction.
*
* @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 control normally returns
* but also when an exception is thrown.
* If this parameter is true, the inserted code cannot
* access local variables.
* @param redundant if true, redundant bytecode will be generated.
* the redundancy is necessary when some compilers (Kotlin?)
* generate the original bytecode.
* The other <code>insertAfter</code> methods calls this method
* with <code>false</code> for this parameter.
* A tip is to pass <code>this.getDeclaringClass().isKotlin()</code>
* to this parameter.
*
* @see CtClass#isKotlin()
* @see #getDeclaringClass()
* @since 3.26
*/
public void insertAfter(String src, boolean asFinally, boolean redundant)
throws CannotCompileException
{
CtClass cc = declaringClass;
cc.checkModify();
ConstPool pool = methodInfo.getConstPool();
CodeAttribute ca = methodInfo.getCodeAttribute();
if (ca == null)
throw new CannotCompileException("no method body");
CodeIterator iterator = ca.iterator();
int retAddr = ca.getMaxLocals();
Bytecode b = new Bytecode(pool, 0, retAddr + 1);
b.setStackDepth(ca.getMaxStack() + 1);
Javac jv = new Javac(b, cc);
try {
int nvars = jv.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
jv.recordParamNames(ca, nvars);
CtClass rtype = getReturnType0();
int varNo = jv.recordReturnType(rtype, true);
jv.recordLocalVariables(ca, 0);
// finally clause for exceptions
int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo,
jv, src);
int handlerPos = iterator.getCodeLength();
if (asFinally)
ca.getExceptionTable().add(getStartPosOfBody(ca), handlerPos, handlerPos, 0);
int adviceLen = 0;
int advicePos = 0;
boolean noReturn = true;
while (iterator.hasNext()) {
int pos = iterator.next();
if (pos >= handlerPos)
break;
int c = iterator.byteAt(pos);
if (c == Opcode.ARETURN || c == Opcode.IRETURN
|| c == Opcode.FRETURN || c == Opcode.LRETURN
|| c == Opcode.DRETURN || c == Opcode.RETURN) {
if (redundant) {
iterator.setMark2(handlerPos);
Bytecode bcode;
Javac jvc;
int retVarNo;
if (noReturn) {
noReturn = false;
bcode = b;
jvc = jv;
retVarNo = varNo;
}
else {
bcode = new Bytecode(pool, 0, retAddr + 1);
bcode.setStackDepth(ca.getMaxStack() + 1);
jvc = new Javac(bcode, cc);
int nvars2 = jvc.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
jvc.recordParamNames(ca, nvars2);
retVarNo = jvc.recordReturnType(rtype, true);
jvc.recordLocalVariables(ca, 0);
}
int adviceLen2 = insertAfterAdvice(bcode, jvc, src, pool, rtype, retVarNo);
int offset = iterator.append(bcode.get());
iterator.append(bcode.getExceptionTable(), offset);
int advicePos2 = iterator.getCodeLength() - adviceLen2;
insertGoto(iterator, advicePos2, pos);
handlerPos = iterator.getMark2();
}
else {
if (noReturn) {
// finally clause for normal termination
adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo);
handlerPos = iterator.append(b.get());
iterator.append(b.getExceptionTable(), handlerPos);
advicePos = iterator.getCodeLength() - adviceLen;
handlerLen = advicePos - handlerPos;
noReturn = false;
}
insertGoto(iterator, advicePos, pos);
advicePos = iterator.getCodeLength() - adviceLen;
handlerPos = advicePos - handlerLen;
}
}
}
if (noReturn) {
handlerPos = iterator.append(b.get());
iterator.append(b.getExceptionTable(), handlerPos);
}
ca.setMaxStack(b.getMaxStack());
ca.setMaxLocals(b.getMaxLocals());
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);
}
}
private int insertAfterAdvice(Bytecode code, Javac jv, String src,
ConstPool cp, CtClass rtype, int varNo)
throws CompileError
{
int pc = code.currentPc();
if (rtype == CtClass.voidType) {
code.addOpcode(Opcode.ACONST_NULL);
code.addAstore(varNo);
jv.compileStmnt(src);
code.addOpcode(Opcode.RETURN);
if (code.getMaxLocals() < 1)
code.setMaxLocals(1);
}
else {
code.addStore(varNo, rtype);
jv.compileStmnt(src);
code.addLoad(varNo, rtype);
if (rtype.isPrimitive())
code.addOpcode(((CtPrimitiveType)rtype).getReturnOp());
else
code.addOpcode(Opcode.ARETURN);
}
return code.currentPc() - pc;
}
/*
* assert subr > pos
*/
private void insertGoto(CodeIterator iterator, int subr, int pos)
throws BadBytecode
{
iterator.setMark(subr);
// the gap length might be a multiple of 4.
iterator.writeByte(Opcode.NOP, pos);
boolean wide = subr + 2 - pos > Short.MAX_VALUE;
int len = wide ? 4 : 2;
CodeIterator.Gap gap = iterator.insertGapAt(pos, len, false);
pos = gap.position + gap.length - len;
int offset = iterator.getMark() - pos;
if (wide) {
iterator.writeByte(Opcode.GOTO_W, pos);
iterator.write32bit(offset, pos + 1);
}
else if (offset <= Short.MAX_VALUE) {
iterator.writeByte(Opcode.GOTO, pos);
iterator.write16bit(offset, pos + 1);
}
else {
if (gap.length < 4) {
CodeIterator.Gap gap2 = iterator.insertGapAt(gap.position, 2, false);
pos = gap2.position + gap2.length + gap.length - 4;
}
iterator.writeByte(Opcode.GOTO_W, pos);
iterator.write32bit(iterator.getMark() - pos, pos + 1);
}
}
/* insert a finally clause
*/
private int insertAfterHandler(boolean asFinally, Bytecode b,
CtClass rtype, int returnVarNo,
Javac javac, String src)
throws CompileError
{
if (!asFinally)
return 0;
int var = b.getMaxLocals();
b.incMaxLocals(1);
int pc = b.currentPc();
b.addAstore(var); // store an exception
if (rtype.isPrimitive()) {
char c = ((CtPrimitiveType)rtype).getDescriptor();
if (c == 'D') {
b.addDconst(0.0);
b.addDstore(returnVarNo);
}
else if (c == 'F') {
b.addFconst(0);
b.addFstore(returnVarNo);
}
else if (c == 'J') {
b.addLconst(0);
b.addLstore(returnVarNo);
}
else if (c == 'V') {
b.addOpcode(Opcode.ACONST_NULL);
b.addAstore(returnVarNo);
}
else { // int, boolean, char, short, ...
b.addIconst(0);
b.addIstore(returnVarNo);
}
}
else {
b.addOpcode(Opcode.ACONST_NULL);
b.addAstore(returnVarNo);
}
javac.compileStmnt(src);
b.addAload(var);
b.addOpcode(Opcode.ATHROW);
return b.currentPc() - pc;
}
/* -- OLD version --
public void insertAfter(String src) throws CannotCompileException {
declaringClass.checkModify();
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(),
Modifier.isStatic(getModifiers()));
CtClass rtype = getReturnType0();
int varNo = jv.recordReturnType(rtype, true);
boolean isVoid = rtype == CtClass.voidType;
if (isVoid) {
b.addOpcode(Opcode.ACONST_NULL);
b.addAstore(varNo);
jv.compileStmnt(src);
}
else {
b.addStore(varNo, rtype);
jv.compileStmnt(src);
b.addLoad(varNo, rtype);
}
byte[] code = b.get();
ca.setMaxStack(b.getMaxStack());
ca.setMaxLocals(b.getMaxLocals());
while (iterator.hasNext()) {
int pos = iterator.next();
int c = iterator.byteAt(pos);
if (c == Opcode.ARETURN || c == Opcode.IRETURN
|| c == Opcode.FRETURN || c == Opcode.LRETURN
|| c == Opcode.DRETURN || c == Opcode.RETURN)
iterator.insert(pos, code);
}
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
catch (CompileError e) {
throw new CannotCompileException(e);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
*/
Adds a catch clause that handles an exception thrown in the
body. The catch clause must end with a return or throw statement.
Params: - src – the source code representing the catch clause.
It must be a single statement or block.
- exceptionType – the type of the exception handled by the
catch clause.
/**
* Adds a catch clause that handles an exception thrown in the
* 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
{
addCatch(src, exceptionType, "$e");
}
Adds a catch clause that handles an exception thrown in the
body. The catch clause must end with a return or throw statement.
Params: - src – the source code representing the catch clause.
It must be a single statement or block.
- exceptionType – the type of the exception handled by the
catch clause.
- exceptionName – the name of the variable containing the
caught exception, for example,
$e
.
/**
* Adds a catch clause that handles an exception thrown in the
* 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.
* @param exceptionName the name of the variable containing the
* caught exception, for example,
* <code>$e</code>.
*/
public void addCatch(String src, CtClass exceptionType,
String exceptionName)
throws CannotCompileException
{
CtClass cc = declaringClass;
cc.checkModify();
ConstPool cp = methodInfo.getConstPool();
CodeAttribute ca = methodInfo.getCodeAttribute();
CodeIterator iterator = ca.iterator();
Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
b.setStackDepth(1);
Javac jv = new Javac(b, cc);
try {
jv.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
int var = jv.recordVariable(exceptionType, exceptionName);
b.addAstore(var);
jv.compileStmnt(src);
int stack = b.getMaxStack();
int locals = b.getMaxLocals();
if (stack > ca.getMaxStack())
ca.setMaxStack(stack);
if (locals > ca.getMaxLocals())
ca.setMaxLocals(locals);
int len = iterator.getCodeLength();
int pos = iterator.append(b.get());
ca.getExceptionTable().add(getStartPosOfBody(ca), len, len,
cp.addClassInfo(exceptionType));
iterator.append(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);
}
}
/* CtConstructor overrides this method.
*/
int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
return 0;
}
Inserts bytecode at the specified line in the body.
It is equivalent to:
insertAt(lineNum, true, src)
See this method as well.
Params: - lineNum – the line number. The bytecode is inserted at the
beginning of the code at the line specified by this
line number.
- src – the source code representing the inserted bytecode.
It must be a single statement or block.
See Also: Returns: the line number at which the bytecode has been inserted.
/**
* Inserts bytecode at the specified line in the body.
* It is equivalent to:
*
* <br><code>insertAt(lineNum, true, src)</code>
*
* <br>See this method as well.
*
* @param lineNum the line number. The bytecode is inserted at the
* beginning of the code at the line specified by this
* line number.
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
* @return the line number at which the bytecode has been inserted.
*
* @see CtBehavior#insertAt(int,boolean,String)
*/
public int insertAt(int lineNum, String src)
throws CannotCompileException
{
return insertAt(lineNum, true, src);
}
Inserts bytecode at the specified line in the body.
If there is not
a statement at the specified line, the bytecode might be inserted
at the line including the first statement after that line specified.
For example, if there is only a closing brace at that line, the
bytecode would be inserted at another line below.
To know exactly where the bytecode will be inserted, call with
modify
set to false
.
Params: - lineNum – the line number. The bytecode is inserted at the
beginning of the code at the line specified by this
line number.
- modify – if false, this method does not insert the bytecode.
It instead only returns the line number at which
the bytecode would be inserted.
- src – the source code representing the inserted bytecode.
It must be a single statement or block.
If modify is false, the value of src can be null.
Returns: the line number at which the bytecode has been inserted.
/**
* Inserts bytecode at the specified line in the body.
*
* <p>If there is not
* a statement at the specified line, the bytecode might be inserted
* at the line including the first statement after that line specified.
* For example, if there is only a closing brace at that line, the
* bytecode would be inserted at another line below.
* To know exactly where the bytecode will be inserted, call with
* <code>modify</code> set to <code>false</code>.
*
* @param lineNum the line number. The bytecode is inserted at the
* beginning of the code at the line specified by this
* line number.
* @param modify if false, this method does not insert the bytecode.
* It instead only returns the line number at which
* the bytecode would be inserted.
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
* If modify is false, the value of src can be null.
* @return the line number at which the bytecode has been inserted.
*/
public int insertAt(int lineNum, boolean modify, String src)
throws CannotCompileException
{
CodeAttribute ca = methodInfo.getCodeAttribute();
if (ca == null)
throw new CannotCompileException("no method body");
LineNumberAttribute ainfo
= (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
if (ainfo == null)
throw new CannotCompileException("no line number info");
LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum);
lineNum = pc.line;
int index = pc.index;
if (!modify)
return lineNum;
CtClass cc = declaringClass;
cc.checkModify();
CodeIterator iterator = ca.iterator();
Javac jv = new Javac(cc);
try {
jv.recordLocalVariables(ca, index);
jv.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
jv.setMaxLocals(ca.getMaxLocals());
jv.compileStmnt(src);
Bytecode b = jv.getBytecode();
int locals = b.getMaxLocals();
int stack = b.getMaxStack();
ca.setMaxLocals(locals);
/* We assume that there is no values in the operand stack
* at the position where the bytecode is inserted.
*/
if (stack > ca.getMaxStack())
ca.setMaxStack(stack);
index = iterator.insertAt(index, b.get());
iterator.insert(b.getExceptionTable(), index);
methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
return lineNum;
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
catch (CompileError e) {
throw new CannotCompileException(e);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
}
}