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.
437 lines
15 KiB
437 lines
15 KiB
5 years ago
|
/*
|
||
|
* 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";
|
||
|
}
|
||
|
}
|
||
|
}
|