ASMSupport教程4.12 生成方法调用操作

Stella981
• 阅读 724

这一节我们讲如何用ASMSupport生成方法调用的操作,方法调用包括下面四种类型:

  1. 调用构造方法
  2. 调用静态方法
  3. 调用非静态方法
  4. 调用当前类的方法
  5. 调用父类方法

首先我们需要看我们想要生成的类:

代码1:

package generated.operators;

import java.io.PrintStream;

public class MethodInvokeOperatorGenerateExample { public String toString() { return "description is "" + super.toString() + """; }

public String description() { return toString(); }

public static String getDescription(MethodInvokeOperatorGenerateExample obj) { return obj.description(); }

public static void main(String[] args) { MethodInvokeOperatorGenerateExample obj = new MethodInvokeOperatorGenerateExample(); System.out.println("Call static method : " + getDescription(obj)); } }

这里面包括了所有的方法调用的类型。

调用构造方法


调用构造方法我们是用的是public final MethodInvoker invokeConstructor(AClass owner, Parameterized... arguments)方法,同样也是ProgramBlock的方法,

这个方法有两个参数:

  1. 表示需要构造的类,这里我们通过调用getMethodOwner获取当前操作类
  2. 表示构造方法的参数。这个参数是个变元参数

在代码1中,我们有MethodInvokeOperatorGenerateExample obj = new MethodInvokeOperatorGenerateExample();代码是调用构造方法,那我们对应的asmsupport的代码如下:

invokeConstructor(getMethodOwner())

这里有个getMethodOwner()方法,这个方法就是获取当前生成的class或修改的class,由于我们正在创建class MethodInvokeOperatorGenerateExample,所以这里getMethodOwner()获得的AClass就是MethodInvokeOperatorGenerateExample

调用静态方法


调用静态方法我们使用的是public final MethodInvoker invokeStatic(AClass owner, String methodName, Parameterized... arguments)方法,

参数

  1. 表示调用的静态方法所属的class
  2. 调用的方法名
  3. 表示方法的参数。这个参数是个变元参数

在代码1中,getDescription方法就是静态方法,而getDescription(obj)正好是调用静态方法。对应的asmsupport的代码如下:

invokeStatic(getMethodOwner(), "getDescription", obj)

这里的obj是上面我们通过构造函数构造的对象,这里作为getDescription的参数。

调用非静态方法


调用静态方法我们使用的是public final MethodInvoker invoke(Parameterized caller, String methodName, Parameterized... arguments)方法,

参数

  1. 表示调用的静态方法所属的对象,这里的类型是 Parameterized ,说明这里可以是LocalVariable或者GlobalVariable类型,或者其他的的Parameterized类型
  2. 调用的方法名
  3. 表示方法的参数。这个参数是个变元参数

在代码1中的getDescription方法内,我们调用了”obj.description();”语句,那么对应的asmsupport的代码如下:

invoke(argus\[0\], "description")

这里的argus\[0\]表示当前方法的第一个参数

调用当前类的方法和调用父类方法


调用当前类和调用父类的方法其实和调用非静态方法很相似,唯一的区别就是我们传入的第一个参数。在java代码中使用this和super关键字表示当前的和父类的,那么我们同样在asmsupport中也有相应的方法:

  1. public final ThisVariable getThis(): 返回的jw.asmsupport.definition.variableThisVariable对象,将这个对象传入invoke方法的第一个参数。
  2. public final SuperVariable getSuper():返回的是jw.asmsupport.definition.variableSuperVariable对象,将这个对象传入invoke方法的第一个参数。

当然,如果当前类中没有重写父类的方法,我们直接调用getThis()方法并将其返回值传入invoke方法作为调用父类方法。这个和java代码中一样的,比如代码1中的代码:

public String description() { return toString(); }

这里面其实就是调用了父类的方法,但是我们这里使用this.toString()或者super.toString()都可以,所以这里我们使用如下asmsupport代码:

/\*this.toString()\*/ invoke(getThis(), "toString") /\*super.toString()\*/ invoke(getSuper(), "toString")

代码1完整ASMSupport代码:


package example.operators;

import org.objectweb.asm.Opcodes;

import jw.asmsupport.block.method.common.CommonMethodBody; import jw.asmsupport.block.method.common.StaticMethodBody; import jw.asmsupport.clazz.AClass; import jw.asmsupport.clazz.AClassFactory; import jw.asmsupport.creator.ClassCreator; import jw.asmsupport.definition.value.Value; import jw.asmsupport.definition.variable.LocalVariable; import jw.asmsupport.operators.method.MethodInvoker;

import example.AbstractExample;

public class MethodInvokeOperatorGenerate extends AbstractExample {

/**
 * @param args
 */
public static void main(String[] args) {
    
    ClassCreator creator = new ClassCreator(Opcodes.V1_5, Opcodes.ACC_PUBLIC , "generated.operators.MethodInvokeOperatorGenerateExample", null, null);

    creator.createMethod("toString", null, null, AClass.STRING_ACLASS, null, Opcodes.ACC_PUBLIC, new CommonMethodBody(){

        @Override
        public void generateBody(LocalVariable... argus) {
            //通常我们将super看作是一个变量所以我们用invoke(Parameterized caller, String methodName, Parameterized... arguments)
            //方法实现super.xxxx。通过getSuper方法获取super变量
            MethodInvoker superToString = invoke(getSuper(), "toString");
            runReturn(append(Value.value("description is \""), superToString, Value.value("\"")));
        }
        
    });
    
    /**
     * 实现如下方法
     * public String description(){
     *    return toString();
     * }
     * 
     */
    creator.createMethod("description", null, null, AClass.STRING_ACLASS, null, Opcodes.ACC_PUBLIC, new CommonMethodBody(){

        @Override
        public void generateBody(LocalVariable... argus) {
            /**
             * 这里和调用super.xxx是一样的。调用当前类中的非静态方法都是通过
             * invoke(Parameterized caller, String methodName, Parameterized... arguments)方法调用。
             * 通过getThis方法获取this变量
             */
            runReturn(invoke(getThis(), "toString"));                
        }
        
    });
    
    /**
     * 生成如下方法的字节码:
     * public static String getDescription(MyObject obj){
     *     return obj.description();
     * }
     */
    creator.createStaticMethod("getDescription", new AClass[]{creator.getCurrentClass()}, new String[]{"obj"}, AClass.STRING_ACLASS, null,
            Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, new StaticMethodBody(){

        @Override
        public void generateBody(LocalVariable... argus) {
            /**
             * 和上面。
             * 这里argus[0]就是我们定义的参数obj。如果需要传递参数可以直接在
             * 方法调用的时候添加需要传递的参数,因为这是个变元方法
             */
            runReturn(invoke(argus[0], "description"));
        }
    });
    
    /**
     * public static void main(String[] args){
     *     MyObject obj = new MyObject();
     *     System.out.println("Call static method : " + MyObject.getDescription(obj));
     * }
     */
    creator.createStaticMethod("main", new AClass[]{AClassFactory.getProductClass(String[].class)}, new String[]{"args"}, null, null,
            Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, new StaticMethodBody(){

        @Override
        public void generateBody(LocalVariable... argus) {
            /**
             * 首先调用构造方法生成MethodInvokeOperatorGenerateExample obj = new MethodInvokeOperatorGenerateExample();
             * 通过invokeConstructor方法实现调用构造方法
             * 这个方法后两个参数。
             * 1.表示需要构造的类,这里我们通过调用getMethodOwner获取当前操作类
             * 2.表示构造方法的参数。这个参数是个变元参数
             */
            LocalVariable obj = createVariable("obj", getMethodOwner(), false, invokeConstructor(getMethodOwner()));
            
            /**
             * 实现System.out.println("Call static method : " + MyObject.getDescription(obj));
             * 这里将学习到如何生成调用静态方法
             * 
             * 调用静态方法是通过invokeStatic(AClass owner, String methodName, Parameterized... arguments)
             * 实现的。
             * 这个方法有三个参数
             * 1.通过那个Class调用静态方法。
             * 2.静态方法的名称
             * 3.参数
             */
            MethodInvoker getDescriptionInvoker = invokeStatic(getMethodOwner(), "getDescription", obj);
            
            invoke(systemOut, "println", append(Value.value("Call static method : "), getDescriptionInvoker));
            
            runReturn();
        }
    });
    generate(creator);
}

}

更多教程

更多教程

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java反射大全
作者对反射的理解:方法的调用(正常的调用:对象.方法()。反射调用方法:方法.对象())静态属性的调用(正常的调用:类.属性。反射调用:属性.类)常见反射的用法:        1.通过反射获取类Class<?demo1Class
Wesley13 Wesley13
3年前
Activiti 工作流入门指南
<divclass"htmledit\_views"id"content\_views"<h1<aname"t0"</a概览</h1<p如我们的介绍部分所述,Activiti目前分为两大类:</p<ul<li<p<ahref"https://activiti.gitbook.io/activiti7deve
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Stella981 Stella981
3年前
ASMSupport教程4.8 生成逻辑运算操作
<p在java中有以下逻辑运算符:</p<ul<li&amp;&amp;:条件与</li<li||:条件或</li<li&amp;:布尔型的逻辑与</li<li|:布尔型的逻辑或</li<li^:布尔型的逻辑异或</li<li!:非操作</li</ul<p那么接下来我们将些段例子
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
ASMSupport教程4.11 生成数组操作
<p在任何语言里,数组都是基本的数据类型,我们这一节将讲述如何生成数组操作。</p<p数组操作包括以下几个:</p<ol<li创建数组</li<li获取数组长度</li<li获取数组每个元素的内容</li<li为数组元素赋值</li</ol<p我们接下来对每种操作进行详解。</p<h3<fonts
Wesley13 Wesley13
3年前
JAVA 并发编程之二:Object对象中的wait,notify,notifyAll 概念+作用(线程状态控制Type1)
<divclass"htmledit\_views"id"content\_views"<pwait,notify,notifyAll是定义在Object类的实例方法,用于控制线程状态。</p<p三个方法都必须在synchronized同步关键字所限定的作用域中调用,否则会报错java.lang.IllegalMonitorStat