scouter.javassist.CtMethod Maven / Gradle / Ivy
/*
* 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 scouter.javassist;
import scouter.javassist.bytecode.AccessFlag;
import scouter.javassist.bytecode.BadBytecode;
import scouter.javassist.bytecode.Bytecode;
import scouter.javassist.bytecode.CodeAttribute;
import scouter.javassist.bytecode.CodeIterator;
import scouter.javassist.bytecode.ConstPool;
import scouter.javassist.bytecode.Descriptor;
import scouter.javassist.bytecode.MethodInfo;
import scouter.javassist.bytecode.Opcode;
/**
* An instance of CtMethod
represents a method.
*
* See the super class CtBehavior
since
* a number of useful methods are in CtBehavior
.
* A number of useful factory methods are in CtNewMethod
.
*
* @see CtClass#getDeclaredMethods()
* @see CtNewMethod
*/
public final class CtMethod extends CtBehavior {
protected String cachedStringRep;
/**
* @see #make(MethodInfo minfo, CtClass declaring)
*/
CtMethod(MethodInfo minfo, CtClass declaring) {
super(declaring, minfo);
cachedStringRep = null;
}
/**
* Creates a public abstract method. The created method must be
* added to a class with CtClass.addMethod()
.
*
* @param declaring the class to which the created method is added.
* @param returnType the type of the returned value
* @param mname the method name
* @param parameters a list of the parameter types
*
* @see CtClass#addMethod(CtMethod)
*/
public CtMethod(CtClass returnType, String mname,
CtClass[] parameters, CtClass declaring) {
this(null, declaring);
ConstPool cp = declaring.getClassFile2().getConstPool();
String desc = Descriptor.ofMethod(returnType, parameters);
methodInfo = new MethodInfo(cp, mname, desc);
setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
}
/**
* Creates a copy of a CtMethod
object.
* The created method must be
* added to a class with CtClass.addMethod()
.
*
*
All occurrences of class names in the created method
* are replaced with names specified by
* map
if map
is not null
.
*
*
For example, suppose that a method at()
is as
* follows:
*
*
* public X at(int i) {
* return (X)super.elementAt(i);
* }
*
* (X
is a class name.) If map
substitutes
* String
for X
, then the created method is:
*
*
* public String at(int i) {
* return (String)super.elementAt(i);
* }
*
* By default, all the occurrences of the names of the class
* declaring at()
and the superclass are replaced
* with the name of the class and the superclass that the
* created method 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#addMethod(CtMethod)
* @see ClassMap#fix(String)
*/
public CtMethod(CtMethod src, CtClass declaring, ClassMap map)
throws CannotCompileException
{
this(null, declaring);
copy(src, false, map);
}
/**
* Compiles the given source code and creates a method.
* This method simply delegates to make()
in
* CtNewMethod
. See it for more details.
* CtNewMethod
has a number of useful factory methods.
*
* @param src the source text.
* @param declaring the class to which the created method is added.
* @see CtNewMethod#make(String, CtClass)
*/
public static CtMethod make(String src, CtClass declaring)
throws CannotCompileException
{
return CtNewMethod.make(src, declaring);
}
/**
* Creates a method from a MethodInfo
object.
*
* @param declaring the class declaring the method.
* @throws CannotCompileException if the the MethodInfo
* object and the declaring class have different
* ConstPool
objects
* @since 3.6
*/
public static CtMethod make(MethodInfo minfo, CtClass declaring)
throws CannotCompileException
{
if (declaring.getClassFile2().getConstPool() != minfo.getConstPool())
throw new CannotCompileException("bad declaring class");
return new CtMethod(minfo, declaring);
}
/**
* Returns a hash code value for the method.
* If two methods have the same name and signature, then
* the hash codes for the two methods are equal.
*/
@Override
public int hashCode() {
return getStringRep().hashCode();
}
/**
* This method is invoked when setName() or replaceClassName()
* in CtClass is called.
*/
@Override
void nameReplaced() {
cachedStringRep = null;
}
/* This method is also called by CtClassType.getMethods0().
*/
final String getStringRep() {
if (cachedStringRep == null)
cachedStringRep = methodInfo.getName()
+ Descriptor.getParamDescriptor(methodInfo.getDescriptor());
return cachedStringRep;
}
/**
* Indicates whether obj
has the same name and the
* same signature as this method.
*/
@Override
public boolean equals(Object obj) {
return obj != null && obj instanceof CtMethod
&& ((CtMethod)obj).getStringRep().equals(getStringRep());
}
/**
* Returns the method name followed by parameter types
* such as scouter.javassist.CtMethod.setBody(String)
.
*
* @since 3.5
*/
@Override
public String getLongName() {
return getDeclaringClass().getName() + "."
+ getName() + Descriptor.toString(getSignature());
}
/**
* Obtains the name of this method.
*/
@Override
public String getName() {
return methodInfo.getName();
}
/**
* Changes the name of this method.
*/
public void setName(String newname) {
declaringClass.checkModify();
methodInfo.setName(newname);
}
/**
* Obtains the type of the returned value.
*/
public CtClass getReturnType() throws NotFoundException {
return getReturnType0();
}
/**
* Returns true if the method body is empty, that is, {}
.
* It also returns true if the method is an abstract method.
*/
@Override
public boolean isEmpty() {
CodeAttribute ca = getMethodInfo2().getCodeAttribute();
if (ca == null) // abstract or native
return (getModifiers() & Modifier.ABSTRACT) != 0;
CodeIterator it = ca.iterator();
try {
return it.hasNext() && it.byteAt(it.next()) == Opcode.RETURN
&& !it.hasNext();
}
catch (BadBytecode e) {}
return false;
}
/**
* Copies a method body from another method.
* If this method is abstract, the abstract modifier is removed
* after the method body is copied.
*
*
All occurrences of the class names in the copied method 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(CtMethod src, ClassMap map)
throws CannotCompileException
{
setBody0(src.declaringClass, src.methodInfo,
declaringClass, methodInfo, map);
}
/**
* Replace a method body with a new method body wrapping the
* given method.
*
* @param mbody the wrapped method
* @param constParam the constant parameter given to
* the wrapped method
* (maybe null
).
*
* @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
*/
public void setWrappedBody(CtMethod mbody, ConstParameter constParam)
throws CannotCompileException
{
declaringClass.checkModify();
CtClass clazz = getDeclaringClass();
CtClass[] params;
CtClass retType;
try {
params = getParameterTypes();
retType = getReturnType();
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
Bytecode code = CtNewWrappedMethod.makeBody(clazz,
clazz.getClassFile2(),
mbody,
params, retType,
constParam);
CodeAttribute cattr = code.toCodeAttribute();
methodInfo.setCodeAttribute(cattr);
methodInfo.setAccessFlags(methodInfo.getAccessFlags()
& ~AccessFlag.ABSTRACT);
// rebuilding a stack map table is not needed.
}
// inner classes
/**
* Instances of this class represent a constant parameter.
* They are used to specify the parameter given to the methods
* created by CtNewMethod.wrapped()
.
*
* @see CtMethod#setWrappedBody(CtMethod,CtMethod.ConstParameter)
* @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
* @see CtNewConstructor#make(CtClass[],CtClass[],int,CtMethod,CtMethod.ConstParameter,CtClass)
*/
public static class ConstParameter {
/**
* Makes an integer constant.
*
* @param i the constant value.
*/
public static ConstParameter integer(int i) {
return new IntConstParameter(i);
}
/**
* Makes a long integer constant.
*
* @param i the constant value.
*/
public static ConstParameter integer(long i) {
return new LongConstParameter(i);
}
/**
* Makes an String
constant.
*
* @param s the constant value.
*/
public static ConstParameter string(String s) {
return new StringConstParameter(s);
}
ConstParameter() {}
/**
* @return the size of the stack consumption.
*/
int compile(Bytecode code) throws CannotCompileException {
return 0;
}
String descriptor() {
return defaultDescriptor();
}
/**
* @see CtNewWrappedMethod
*/
static String defaultDescriptor() {
return "([Ljava/lang/Object;)Ljava/lang/Object;";
}
/**
* Returns the descriptor for constructors.
*
* @see CtNewWrappedConstructor
*/
String constDescriptor() {
return defaultConstDescriptor();
}
/**
* Returns the default descriptor for constructors.
*/
static String defaultConstDescriptor() {
return "([Ljava/lang/Object;)V";
}
}
static class IntConstParameter extends ConstParameter {
int param;
IntConstParameter(int i) {
param = i;
}
@Override
int compile(Bytecode code) throws CannotCompileException {
code.addIconst(param);
return 1;
}
@Override
String descriptor() {
return "([Ljava/lang/Object;I)Ljava/lang/Object;";
}
@Override
String constDescriptor() {
return "([Ljava/lang/Object;I)V";
}
}
static class LongConstParameter extends ConstParameter {
long param;
LongConstParameter(long l) {
param = l;
}
@Override
int compile(Bytecode code) throws CannotCompileException {
code.addLconst(param);
return 2;
}
@Override
String descriptor() {
return "([Ljava/lang/Object;J)Ljava/lang/Object;";
}
@Override
String constDescriptor() {
return "([Ljava/lang/Object;J)V";
}
}
static class StringConstParameter extends ConstParameter {
String param;
StringConstParameter(String s) {
param = s;
}
@Override
int compile(Bytecode code) throws CannotCompileException {
code.addLdc(param);
return 1;
}
@Override
String descriptor() {
return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
}
@Override
String constDescriptor() {
return "([Ljava/lang/Object;Ljava/lang/String;)V";
}
}
}