/*
* 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.
BadBytecode;
import javassist.bytecode.
Bytecode;
import javassist.bytecode.
CodeAttribute;
import javassist.bytecode.
CodeIterator;
import javassist.bytecode.
ConstPool;
import javassist.bytecode.
Descriptor;
import javassist.bytecode.
MethodInfo;
import javassist.bytecode.
Opcode;
/**
* An instance of <code>CtMethod</code> represents a method.
*
* <p>See the super class <code>CtBehavior</code> since
* a number of useful methods are in <code>CtBehavior</code>.
* A number of useful factory methods are in <code>CtNewMethod</code>.
*
* @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 <code>CtClass.addMethod()</code>.
*
* @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 <code>CtMethod</code> object.
* The created method must be
* added to a class with <code>CtClass.addMethod()</code>.
*
* <p>All occurrences of class names in the created method
* are replaced with names specified by
* <code>map</code> if <code>map</code> is not <code>null</code>.
*
* <p>For example, suppose that a method <code>at()</code> is as
* follows:
*
* <pre>
* public X at(int i) {
* return (X)super.elementAt(i);
* }</pre>
*
* <p>(<code>X</code> is a class name.) If <code>map</code> substitutes
* <code>String</code> for <code>X</code>, then the created method is:
*
* <pre>
* public String at(int i) {
* return (String)super.elementAt(i);
* }</pre>
*
* <p>By default, all the occurrences of the names of the class
* declaring <code>at()</code> 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 <code>map</code> is null or not.
* To prevent this replacement, call <code>ClassMap.fix()</code>
* or <code>put()</code> to explicitly specify replacement.
*
* <p><b>Note:</b> if the <code>.class</code> notation (for example,
* <code>String.class</code>) 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 <code>Class.forName()</code> 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 <code>null</code>.
*
* @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 <code>make()</code> in
* <code>CtNewMethod</code>. See it for more details.
* <code>CtNewMethod</code> 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 <code>MethodInfo</code> object.
*
* @param declaring the class declaring the method.
* @throws CannotCompileException if the the <code>MethodInfo</code>
* object and the declaring class have different
* <code>ConstPool</code> 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 <code>obj</code> 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 <code>javassist.CtMethod.setBody(String)</code>.
*
* @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, <code>{}</code>.
* 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.
*
* <p>All occurrences of the class names in the copied method body
* are replaced with the names specified by
* <code>map</code> if <code>map</code> is not <code>null</code>.
*
* @param src the method that the body is copied from.
* @param map the hashtable associating original class names
* with substituted names.
* It can be <code>null</code>.
*/
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 <code>null</code>).
*
* @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 <code>CtNewMethod.wrapped()</code>.
*
* @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 <code>String</code> 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";
}
}
}