You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
436 lines
15 KiB
436 lines
15 KiB
/* |
|
* 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 com.fr.third.javassist; |
|
|
|
import com.fr.third.javassist.bytecode.CodeAttribute; |
|
|
|
/** |
|
* 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 com.fr.third.javassist.CtClass#getDeclaredMethods() |
|
* @see CtNewMethod |
|
*/ |
|
public final class CtMethod extends CtBehavior { |
|
protected String cachedStringRep; |
|
|
|
/** |
|
* @see #make(com.fr.third.javassist.bytecode.MethodInfo minfo, com.fr.third.javassist.CtClass declaring) |
|
*/ |
|
CtMethod(com.fr.third.javassist.bytecode.MethodInfo minfo, com.fr.third.javassist.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 com.fr.third.javassist.CtClass#addMethod(CtMethod) |
|
*/ |
|
public CtMethod(com.fr.third.javassist.CtClass returnType, String mname, |
|
com.fr.third.javassist.CtClass[] parameters, com.fr.third.javassist.CtClass declaring) { |
|
this(null, declaring); |
|
com.fr.third.javassist.bytecode.ConstPool cp = declaring.getClassFile2().getConstPool(); |
|
String desc = com.fr.third.javassist.bytecode.Descriptor.ofMethod(returnType, parameters); |
|
methodInfo = new com.fr.third.javassist.bytecode.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: |
|
* |
|
* <ul><pre>public X at(int i) { |
|
* return (X)super.elementAt(i); |
|
* }</pre></ul> |
|
* |
|
* <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: |
|
* |
|
* <ul><pre>public String at(int i) { |
|
* return (String)super.elementAt(i); |
|
* }</pre></ul> |
|
* |
|
* <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 com.fr.third.javassist.CtClass#addMethod(CtMethod) |
|
* @see com.fr.third.javassist.ClassMap#fix(String) |
|
*/ |
|
public CtMethod(CtMethod src, com.fr.third.javassist.CtClass declaring, com.fr.third.javassist.ClassMap map) |
|
throws com.fr.third.javassist.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, com.fr.third.javassist.CtClass) |
|
*/ |
|
public static CtMethod make(String src, com.fr.third.javassist.CtClass declaring) |
|
throws com.fr.third.javassist.CannotCompileException |
|
{ |
|
return CtNewMethod.make(src, declaring); |
|
} |
|
|
|
/** |
|
* Creates a method from a <code>MethodInfo</code> object. |
|
* |
|
* @param declaring the class declaring the method. |
|
* @throws com.fr.third.javassist.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(com.fr.third.javassist.bytecode.MethodInfo minfo, com.fr.third.javassist.CtClass declaring) |
|
throws com.fr.third.javassist.CannotCompileException |
|
{ |
|
if (declaring.getClassFile2().getConstPool() != minfo.getConstPool()) |
|
throw new com.fr.third.javassist.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. |
|
*/ |
|
public int hashCode() { |
|
return getStringRep().hashCode(); |
|
} |
|
|
|
/** |
|
* This method is invoked when setName() or replaceClassName() |
|
* in CtClass is called. |
|
*/ |
|
void nameReplaced() { |
|
cachedStringRep = null; |
|
} |
|
|
|
/* This method is also called by CtClassType.getMethods0(). |
|
*/ |
|
final String getStringRep() { |
|
if (cachedStringRep == null) |
|
cachedStringRep = methodInfo.getName() |
|
+ com.fr.third.javassist.bytecode.Descriptor.getParamDescriptor(methodInfo.getDescriptor()); |
|
|
|
return cachedStringRep; |
|
} |
|
|
|
/** |
|
* Indicates whether <code>obj</code> has the same name and the |
|
* same signature as this method. |
|
*/ |
|
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 |
|
*/ |
|
public String getLongName() { |
|
return getDeclaringClass().getName() + "." |
|
+ getName() + com.fr.third.javassist.bytecode.Descriptor.toString(getSignature()); |
|
} |
|
|
|
/** |
|
* Obtains the name of this method. |
|
*/ |
|
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 com.fr.third.javassist.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. |
|
*/ |
|
public boolean isEmpty() { |
|
com.fr.third.javassist.bytecode.CodeAttribute ca = getMethodInfo2().getCodeAttribute(); |
|
if (ca == null) // abstract or native |
|
return (getModifiers() & Modifier.ABSTRACT) != 0; |
|
|
|
com.fr.third.javassist.bytecode.CodeIterator it = ca.iterator(); |
|
try { |
|
return it.hasNext() && it.byteAt(it.next()) == com.fr.third.javassist.bytecode.Opcode.RETURN |
|
&& !it.hasNext(); |
|
} |
|
catch (com.fr.third.javassist.bytecode.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 com.fr.third.javassist.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(com.fr.third.javassist.CtClass,String, com.fr.third.javassist.CtClass[], com.fr.third.javassist.CtClass[],CtMethod,CtMethod.ConstParameter, com.fr.third.javassist.CtClass) |
|
*/ |
|
public void setWrappedBody(CtMethod mbody, ConstParameter constParam) |
|
throws com.fr.third.javassist.CannotCompileException |
|
{ |
|
declaringClass.checkModify(); |
|
|
|
com.fr.third.javassist.CtClass clazz = getDeclaringClass(); |
|
com.fr.third.javassist.CtClass[] params; |
|
com.fr.third.javassist.CtClass retType; |
|
try { |
|
params = getParameterTypes(); |
|
retType = getReturnType(); |
|
} |
|
catch (NotFoundException e) { |
|
throw new com.fr.third.javassist.CannotCompileException(e); |
|
} |
|
|
|
com.fr.third.javassist.bytecode.Bytecode code = CtNewWrappedMethod.makeBody(clazz, |
|
clazz.getClassFile2(), |
|
mbody, |
|
params, retType, |
|
constParam); |
|
CodeAttribute cattr = code.toCodeAttribute(); |
|
methodInfo.setCodeAttribute(cattr); |
|
methodInfo.setAccessFlags(methodInfo.getAccessFlags() |
|
& ~com.fr.third.javassist.bytecode.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(com.fr.third.javassist.CtClass,String, com.fr.third.javassist.CtClass[], com.fr.third.javassist.CtClass[],CtMethod,CtMethod.ConstParameter, com.fr.third.javassist.CtClass) |
|
* @see CtNewConstructor#make(com.fr.third.javassist.CtClass[], com.fr.third.javassist.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(com.fr.third.javassist.bytecode.Bytecode code) throws com.fr.third.javassist.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; |
|
} |
|
|
|
int compile(com.fr.third.javassist.bytecode.Bytecode code) throws com.fr.third.javassist.CannotCompileException { |
|
code.addIconst(param); |
|
return 1; |
|
} |
|
|
|
String descriptor() { |
|
return "([Ljava/lang/Object;I)Ljava/lang/Object;"; |
|
} |
|
|
|
String constDescriptor() { |
|
return "([Ljava/lang/Object;I)V"; |
|
} |
|
} |
|
|
|
static class LongConstParameter extends ConstParameter { |
|
long param; |
|
|
|
LongConstParameter(long l) { |
|
param = l; |
|
} |
|
|
|
int compile(com.fr.third.javassist.bytecode.Bytecode code) throws com.fr.third.javassist.CannotCompileException { |
|
code.addLconst(param); |
|
return 2; |
|
} |
|
|
|
String descriptor() { |
|
return "([Ljava/lang/Object;J)Ljava/lang/Object;"; |
|
} |
|
|
|
String constDescriptor() { |
|
return "([Ljava/lang/Object;J)V"; |
|
} |
|
} |
|
|
|
static class StringConstParameter extends ConstParameter { |
|
String param; |
|
|
|
StringConstParameter(String s) { |
|
param = s; |
|
} |
|
|
|
int compile(com.fr.third.javassist.bytecode.Bytecode code) throws CannotCompileException { |
|
code.addLdc(param); |
|
return 1; |
|
} |
|
|
|
String descriptor() { |
|
return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"; |
|
} |
|
|
|
String constDescriptor() { |
|
return "([Ljava/lang/Object;Ljava/lang/String;)V"; |
|
} |
|
} |
|
}
|
|
|