Java ASM系列:(041)InstructionAdapter介绍
本文属于[Java ASM系列一:Core API](https://blog.51cto.com/lsieun/2924583)当中的一篇。
对于`InstructionAdapter`类来说,它的特点是“添加了许多与opcode同名的方法”,更接近“原汁原味”的JVM Instruction Set。
## 1. 为什么有InstructionAdapter类
`InstructionAdapter`类继承自`MethodVisitor`类,它提供了更详细的API用于generate和transform。
在JVM Specification中,一共定义了200多个opcode,在ASM的`MethodVisitor`类当中定义了15个`visitXxxInsn()`方法。这说明一个问题,也就是在`MethodVisitor`类的每个`visitXxxInsn()`方法都会对应JVM Specification当中多个opcode。
那么,`InstructionAdapter`类起到一个什么样的作用呢?`InstructionAdapter`类继承了`MethodVisitor`类,也就继承了那些`visitXxxInsn()`方法,同时它也添加了80多个新的方法,这些新的方法与opcode更加接近。
从功能上来说,`InstructionAdapter`类和`MethodVisitor`类是一样的,两者没有差异。对于`InstructionAdapter`类来说,它可能更适合于熟悉opcode的人来使用。但是,如果我们已经熟悉`MethodVisitor`类里的`visitXxxInsn()`方法,那就完全可以不去使用`InstructionAdapter`类。
## 2. InstructionAdapter类
### 2.1 class info
第一个部分,`InstructionAdapter`类的父类是`MethodVisitor`类。
```java
public class InstructionAdapter extends MethodVisitor {
}
```
### 2.2 fields
第二个部分,`InstructionAdapter`类定义的字段有哪些。我们可以看到,`InstructionAdapter`类定义了一个`OBJECT_TYPE`静态字段。
```java
public class InstructionAdapter extends MethodVisitor {
public static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;");
}
```
### 2.3 constructors
第三个部分,`InstructionAdapter`类定义的构造方法有哪些。
```java
public class InstructionAdapter extends MethodVisitor {
public InstructionAdapter(final MethodVisitor methodVisitor) {
this(Opcodes.ASM9, methodVisitor);
if (getClass() != InstructionAdapter.class) {
throw new IllegalStateException();
}
}
protected InstructionAdapter(final int api, final MethodVisitor methodVisitor) {
super(api, methodVisitor);
}
}
```
### 2.4 methods
第四个部分,`InstructionAdapter`类定义的方法有哪些。除了从`MethodVisitor`类继承的`visitXxxInsn()`方法,`InstructionAdapter`类还定义了许多与opcode相关的新方法,这些新方法本质上就是调用`visitXxxInsn()`方法来实现的。
```java
public class InstructionAdapter extends MethodVisitor {
public void nop() {
mv.visitInsn(Opcodes.NOP);
}
public void aconst(final Object value) {
if (value == null) {
mv.visitInsn(Opcodes.ACONST_NULL);
} else {
mv.visitLdcInsn(value);
}
}
// ......
}
```
## 3. 示例
### 3.1 预期目标
我们想实现的预期目标:生成如下的`HelloWorld`类。
```java
public class HelloWorld {
public void test() {
System.out.println("Hello World");
}
}
```
### 3.2 编码实现
```java
import lsieun.utils.FileUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import static org.objectweb.asm.Opcodes.*;
public class InstructionAdapterExample01 {
public static void main(String[] args) throws Exception {
String relative_path = "sample/HelloWorld.class";
String filepath = FileUtils.getFilePath(relative_path);
// (1) 生成byte[]内容
byte[] bytes = dump();
// (2) 保存byte[]到文件
FileUtils.writeBytes(filepath, bytes);
}
public static byte[] dump() throws Exception {
// (1) 创建ClassWriter对象
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// (2) 调用visitXxx()方法
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "sample/HelloWorld",
null, "java/lang/Object", null);
{
MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
InstructionAdapter ia = new InstructionAdapter(mv1);
ia.visitCode();
ia.load(0, InstructionAdapter.OBJECT_TYPE);
ia.invokespecial("java/lang/Object", "", "()V", false);
ia.areturn(Type.VOID_TYPE);
ia.visitMaxs(1, 1);
ia.visitEnd();
}
{
MethodVisitor mv2 = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
InstructionAdapter ia = new InstructionAdapter(mv2);
ia.visitCode();
ia.getstatic("java/lang/System", "out", "Ljava/io/PrintStream;");
ia.aconst("Hello World");
ia.invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
ia.areturn(Type.VOID_TYPE);
ia.visitMaxs(2, 1);
ia.visitEnd();
}
cw.visitEnd();
// (3) 调用toByteArray()方法
return cw.toByteArray();
}
}
```
## 4. 总结
本文对`InstructionAdapter`类进行了介绍,内容总结如下:
- 第一点,`InstructionAdapter`类的特点就是引入了一些与opcode有关的新方法,这些新方法本质上还是调用`MethodVisitor.visitXxxInsn()`来实现的。
- 第二点,如果已经熟悉`MethodVisitor`类的使用,可以完全不考虑使用`InstructionAdapter`类。