JAVA使用ASM实现lambda表达式的字节码

本文详细介绍了如何使用ASM库动态生成Java字节码,以实现一个lambda表达式的方法,并通过对比反编译的结果,解释了lambda在JVM层面上的表示,涉及到invokeDynamic指令、CallSite和匿名内部类。同时,提到了IDEA中的FernFlower反编译器在展示lambda时的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近想捣鼓一下ASM,于是乎自己想出这么个事情来干,纯粹为了解闷。

先说说最终我们要实现的目标,反编译ASM构建好的字节码,能够出现如下图所示的效果:

    public void lambda(){
             IntStream.range(0,10).forEach(index->System.out.println("this is "+index));
    }

这是一个非常简单的lambda表达式的方法,话不多说,我们开始编码。

    public byte[] writeLambda(){
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        //随便给类取个名字
        cw.visit(V1_8,ACC_PUBLIC,"demo/HelloLambda",null,"java/lang/Object",null);
        //无参构造
        init(cw);
        //创建主要方法
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"writeLambda","()V",null,null);
        mv.visitCode();
        mv.visitInsn(ICONST_0);
        mv.visitIntInsn(BIPUSH,10);
        mv.visitMethodInsn(INVOKESTATIC,"java/util/stream/IntStream","range","(II)Ljava/util/stream/IntStream",false);
        mv.visitMethodInsn(INVOKEINTERFACE,"java/util/stream/IntStream","forEach","(Ljava/util/function/IntConsumer;)V",true);
        mv.visitInsn(RETURN);
        mv.visitEnd();
        return cw.toByteArray();
    }

先写一个大概的框架,缺少的部分也非常明显。动态调用指令和匿名内部类作为方法调用点承载等部分还没有构建,所以这样直接编译出来的类肯定是不行的,如下便是idea直接反编译出来的代码:

public class HelloLambda {
    public HelloLambda() {
    }

    public void writeLambda() {
        // $FF: Couldn't be decompiled
    }
}

明显是错的,那么接下来我们把重要的那部分补充完整。

总所周知,lambda表达式属于java语言层面的东西,而asm所操作的class文件,也就是javac对java文件编译后产生的字节码文件(此时已经不存在lambda表达式了),它属于jvm层面的东西,在jvm视角来看没有什么lambda表达式,它看到的只有invokeDynamic指令,CallSite方法调用点以及一个匿名内部类等这些东西罢了。

先仔细观察一下字节码:

 public void lambda();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: iconst_0
         1: bipush        10
         3: invokestatic  #5                  // InterfaceMethod java/util/stream/IntStream.range:(II)Ljava/util/stream/IntStream;
         6: invokedynamic #6,  0              // InvokeDynamic #0:accept:()Ljava/util/function/IntConsumer;
        11: invokeinterface #7,  2            // InterfaceMethod java/util/stream/IntStream.forEach:(Ljava/util/function/IntConsumer;)V
        16: return
      LineNumberTable:
        line 13: 0
        line 14: 16
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      17     0  this   Lorg/example/asm/bbq;

  private static void lambda$lambda$0(int);
    descriptor: (I)V
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #8                  // Field java/lang/System.out:Ljava/io
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自远方的猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值