asm指令

We are all in the gutter, but some of us are looking at the stars. (我们都生活在阴沟里,但仍有人仰望星空 )- 王尔德 《温德米尔夫人的扇子》

前言
ASM是一种通用Java字节码操作和分析框架。它可以用于修改现有的class文件或动态生成class文件。

ASM is an all purpose Java bytecode manipulation and analysis framework. It can be used to modify existing classes or to dynamically generate classes, directly in binary form. ASM provides some common bytecode transformations and analysis algorithms from which custom complex transformations and code analysis tools can be built. ASM offers similar functionality as other Java bytecode frameworks, but is focused onperformance. Because it was designed and implemented to be as small and as fast as possible, it is well suited for use in dynamic systems (but can of course be used in a static way too, e.g. in compilers).
本篇文章分享的是对ASM的理解和应用,之前需要我们掌握class字节码,JVM基于栈的设计模式,JVM指令

class字节码
我们编写的java文件,会通过javac命令编译为class文件,JVM最终会执行该类型文件来运行程序。下图所示为class文件结构(图片来源网络)。

下面我们通过一个简单的实例来进行说明。下面是我们编写的一个简单的java文件,只是简单的函数调用.

public class Test {
private int num1 = 1;
public static int NUM1 = 100;
public int func(int a,int b){
return add(a,b);
}
public int add(int a,int b) {
return a+b+num1;
}
public int sub(int a, int b) {
return a-b-NUM1;
}
}
使用javac -g Test.java编译为class文件,然后通过 javap -verbose Test.class 命令查看class文件格式。

public class com.wuba.asmdemo.Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#26 // java/lang/Object.""😦)V
#2 = Fieldref #5.#27 // com/wuba/asmdemo/Test.num1:I
#3 = Methodref #5.#28 // com/wuba/asmdemo/Test.add:(II)I
#4 = Fieldref #5.#29 // com/wuba/asmdemo/Test.NUM1:I
#5 = Class #30 // com/wuba/asmdemo/Test
#6 = Class #31 // java/lang/Object
#7 = Utf8 num1
#8 = Utf8 I
#9 = Utf8 NUM1
#10 = Utf8
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 Lcom/wuba/asmdemo/Test;
#17 = Utf8 func
#18 = Utf8 (II)I
#19 = Utf8 a
#20 = Utf8 b
#21 = Utf8 add
#22 = Utf8 sub
#23 = Utf8
#24 = Utf8 SourceFile
#25 = Utf8 Test.java
#26 = NameAndType #10:#11 // “”😦)V
#27 = NameAndType #7:#8 // num1:I
#28 = NameAndType #21:#18 // add:(II)I
#29 = NameAndType #9:#8 // NUM1:I
#30 = Utf8 com/wuba/asmdemo/Test
#31 = Utf8 java/lang/Object
{
public static int NUM1;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC

public com.wuba.asmdemo.Test(); //构造函数
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object.""😦)V
4: aload_0
5: iconst_1
6: putfield #2 // Field num1:I
9: return
LineNumberTable:
line 3: 0
line 5: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/wuba/asmdemo/Test;

public int func(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: iload_1
2: iload_2
3: invokevirtual #3 // Method add:(II)I
6: ireturn
LineNumberTable:
line 10: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/wuba/asmdemo/Test;
0 7 1 a I
0 7 2 b I

public int add(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: iload_1
1: iload_2
2: iadd
3: aload_0
4: getfield #2 // Field num1:I
7: iadd
8: ireturn
LineNumberTable:
line 14: 0
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/wuba/asmdemo/Test;
0 9 1 a I
0 9 2 b I

public int sub(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: iload_1
1: iload_2
2: isub
3: getstatic #4 // Field NUM1:I
6: isub
7: ireturn
LineNumberTable:
line 18: 0
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lcom/wuba/asmdemo/Test;
0 8 1 a I
0 8 2 b I

static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 100
2: putstatic #4 // Field NUM1:I
5: return
LineNumberTable:
line 7: 0
}
SourceFile: “Test.java”
可以看出在编译为class文件后,字段名称,方法名称,类型名称等均在常量池中存在的。从而做到减小文件的目的。同时方法定义也转变为了jvm指令。下面我们需要对jvm指令加深一下了解。在了解之前需要我们理解JVM基于栈的设计模式

JVM基于栈的设计模式
JVM的指令集是基于栈而不是寄存器,基于栈可以具备很好的跨平台性。在线程中执行一个方法时,我们会创建一个栈帧入栈并执行,如果该方法又调用另一个方法时会再次创建新的栈帧然后入栈,方法返回之际,原栈帧会返回方法的执行结果给之前的栈帧,随后虚拟机将会丢弃此栈帧。

局部变量表
局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。虚拟机通过索引定位的方法查找相应的局部变量。举个例子。以上述的代码为例

public int sub(int a, int b) {
return a-b-NUM1;
}
这个方法大家可以猜测一下局部变量有哪些? 答案是3个,不应该只有a,b吗?还有this,对应实例对象方法编译器都会追加一个this参数。如果该方法为静态方法则为2个了。

public int sub(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: iload_1
1: iload_2
2: isub
3: getstatic #4 // Field NUM1:I
6: isub
7: ireturn
LineNumberTable:
line 18: 0
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lcom/wuba/asmdemo/Test;
0 8 1 a I
0 8 2 b I
所以局部变量表第0个元素为this, 第一个为a,第二个为b

操作数栈
通过局部变量表我们有了要操作和待更新的数据,我们如果对局部变量这些数据进行操作呢?通过操作数栈。当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。一个完整的方法执行期间往往包含多个这样出栈/入栈的过程。

JVM指令
load 命令:用于将局部变量表的指定位置的相应类型变量加载到操作数栈顶;
store命令:用于将操作数栈顶的相应类型数据保入局部变量表的指定位置;
invokevirtual:调用实例方法
ireturn: 当前方法返回int
在举个例子

a = b + c 的字节码执行过程中操作数栈以及局部变量表的变化如下图所示

ASM操作
通过上面的介绍,我们对字节码和JVM指令有了进一步的了解,下面我们看一下ASM是如果编辑class字节码的。

ASM API
ASM API基于访问者模式,为我们提供了ClassVisitor,MethodVisitor,FieldVisitor API接口,每当ASM扫描到类字段是会回调visitField方法,扫描到类方法是会回调MethodVisitor,下面我们看一下API接口

ClassVisitor方法解析

public abstract class ClassVisitor {

public void visit(int version, int access, String name, String signature, String superName, String[] interfaces);
//访问类字段时回调
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value);
//访问类方法是回调
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions);
public void visitEnd();
}
MethodVisitor方法解析
public abstract class MethodVisitor {

public void visitParameter(String name, int access);
//访问本地变量类型指令 操作码可以是LOAD,STORE,RET中一种;
public void visitIntInsn(int opcode, int operand);
//域操作指令,用来加载或者存储对象的Field
public void visitFieldInsn(int opcode, String owner, String name, String descriptor);
//访问方法操作指令
public void visitMethodInsn(int opcode, String owner, String name, String descriptor);
public void visitEnd();
}
ASM 使用Demo
java源码

public int add(int a,int b) {
return a+b+num1;
}
class字节码

public int add(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: iload_1
1: iload_2
2: iadd
3: aload_0
4: getfield #2 // Field num1:I
7: iadd
8: ireturn
LineNumberTable:
line 14: 0
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/wuba/asmdemo/Test;
0 9 1 a I
0 9 2 b I
ASM对应的API

        mv = cw.visitMethod(ACC_PUBLIC, "add", "(II)I", null, null);
        mv.visitCode();
        mv.visitVarInsn(ILOAD, 1);
        mv.visitVarInsn(ILOAD, 2);
        mv.visitInsn(IADD);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, "com/wuba/asmdemo/Test", "num1", "I");
        mv.visitInsn(IADD);
        mv.visitInsn(IRETURN);
        Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitLocalVariable("this", "Lcom/wuba/asmdemo/Test;", null, l0, l1, 0);
        mv.visitLocalVariable("a", "I", null, l0, l1, 1);
        mv.visitLocalVariable("b", "I", null, l0, l1, 2);
        mv.visitMaxs(2, 3);
        mv.visitEnd();

可以看出ASM是在指令层次上操作字节码的,和class字节码更加接近。如果我们有些字节码操作的需求,ASM一定可以实现的。只是使用起来比较麻烦一些。这里强烈推荐一款ASM插件ASM ByteCode Outline。 可以一键生成对应的ASM API代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ASM指令机器码对照表是一种将汇编语言指令与对应的机器码进行一一对照的表格,用于指导程序员将高级语言编写的指令转化为计算机可以执行的二进制代码。每条汇编指令都有其对应的机器码,在计算机中被理解为一系列的0和1。 ASM指令机器码对照表的编制根据不同的体系结构和指令集架构而有所差异。例如,对于x86架构的计算机,汇编语言指令与机器码的对照表由处理器制造商提供,并根据不同的处理器型号和指令集进行分类。 在ASM指令机器码对照表中,通常会列出每条汇编指令的助记符(mnemonic)、操作码(opcode)以及操作数(operand)等信息。助记符用来表示一条汇编指令的操作类型,操作码表示该指令在机器码中所对应的二进制数值,而操作数则表示指令要操作的寄存器、内存地址或立即数等。 通过查表,程序员可以将汇编指令翻译为对应的机器码,从而编写出可执行的机器指令代码。在编写汇编语言程序时,程序员需要参考ASM指令机器码对照表,将高级语言指令翻译为对应的机器码,以便计算机能够准确地执行程序。 总之,ASM指令机器码对照表是程序员编写汇编语言程序的重要参考工具,它能够帮助程序员将高级语言指令转化为计算机可以识别和执行的机器码,从而实现程序的功能。了解和掌握ASM指令机器码对照表的使用方法对于汇编语言程序员而言至关重要。 ### 回答2: ASM指令机器码对照表是一种将汇编语言指令与相应机器码进行映射的参考表格。在计算机系统中,计算机只能理解和执行机器码,而汇编语言是与机器码直接对应的低级语言。汇编语言指令是用简单易懂的助记符表示的,但是计算机需要将这些指令转换为二进制的机器码才能执行。 ASM指令机器码对照表的目的是提供一种快速查询的工具,以便程序员在编写汇编代码时能够轻松地确定每个指令的对应机器码。该表通常以表格形式列出汇编指令和对应的机器码。 例如,对于一条MOV指令(将数据从一个位置复制到另一个位置),在ASM指令机器码对照表中,我们可以找到对应的机器码,如MOV R1, R2的机器码可能是1010 0000 0001 0010,具体受到计算机体系结构和编译器的影响。 在编写汇编程序时,程序员可以通过查阅ASM指令机器码对照表,将汇编指令转换为机器码,进而编写出正确的机器码指令。这对于编写底层程序或进行调试十分重要。 总之,ASM指令机器码对照表是一种帮助程序员将汇编指令转化为机器码的参考工具,使得编写汇编语言程序更加高效、准确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值