Java ASM系列:(038)GeneratorAdapter介绍
本文属于[Java ASM系列一:Core API](https://blog.51cto.com/lsieun/2924583)当中的一篇。
对于`GeneratorAdapter`类来说,它非常重要的一个特点:将一些`visitXxxInsn()`方法封装成一些常用的方法。
## 1. GeneratorAdapter类
### 1.1 class info
第一个部分,`GeneratorAdapter`类继承自`LocalVariablesSorter`类。
- org.objectweb.asm.MethodVisitor
- org.objectweb.asm.commons.LocalVariablesSorter
- org.objectweb.asm.commons.GeneratorAdapter
- org.objectweb.asm.commons.AdviceAdapter
```java
public class GeneratorAdapter extends LocalVariablesSorter {
}
```
### 1.2 fields
第二个部分,`GeneratorAdapter`类定义的字段有哪些。
```java
public class GeneratorAdapter extends LocalVariablesSorter {
private final int access;
private final String name;
private final Type returnType;
private final Type[] argumentTypes;
}
```
### 1.3 constructors
第三个部分,`GeneratorAdapter`类定义的构造方法有哪些。
```java
public class GeneratorAdapter extends LocalVariablesSorter {
public GeneratorAdapter(final MethodVisitor methodVisitor,
final int access, final String name, final String descriptor) {
this(Opcodes.ASM9, methodVisitor, access, name, descriptor);
}
protected GeneratorAdapter(final int api, final MethodVisitor methodVisitor,
final int access, final String name, final String descriptor) {
super(api, access, descriptor, methodVisitor);
this.access = access;
this.name = name;
this.returnType = Type.getReturnType(descriptor);
this.argumentTypes = Type.getArgumentTypes(descriptor);
}
}
```
### 1.4 methods
第四个部分,`GeneratorAdapter`类定义的方法有哪些。
```java
public class GeneratorAdapter extends LocalVariablesSorter {
public int getAccess() {
return access;
}
public String getName() {
return name;
}
public Type getReturnType() {
return returnType;
}
public Type[] getArgumentTypes() {
return argumentTypes.clone();
}
}
```
## 2. 特殊方法举例
`GeneratorAdapter`类的特点是将一些`visitXxxInsn()`方法封装成一些常用的方法。在这里,我们给大家举几个有代表性的例子,更多的方法可以参考`GeneratorAdapter`类的源码。
### 2.1 loadThis
在`GeneratorAdapter`类当中,`loadThis()`方法的本质是`mv.visitVarInsn(Opcodes.ALOAD, 0)`;但是,要注意static方法并不需要`this`变量。
```java
public class GeneratorAdapter extends LocalVariablesSorter {
public void loadThis() {
if ((access & Opcodes.ACC_STATIC) != 0) { // 注意,静态方法没有this
throw new IllegalStateException("no 'this' pointer within static method");
}
mv.visitVarInsn(Opcodes.ALOAD, 0);
}
}
```
### 2.2 arg
在`GeneratorAdapter`类当中,定义了一些与方法参数相关的方法。
```java
public class GeneratorAdapter extends LocalVariablesSorter {
private int getArgIndex(final int arg) {
int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
private void loadInsn(final Type type, final int index) {
mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
}
private void storeInsn(final Type type, final int index) {
mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
}
public void loadArg(final int arg) {
loadInsn(argumentTypes[arg], getArgIndex(arg));
}
public void loadArgs(final int arg, final int count) {
int index = getArgIndex(arg);
for (int i = 0; i < count; ++i) {
Type argumentType = argumentTypes[arg + i];
loadInsn(argumentType, index);
index += argumentType.getSize();
}
}
public void loadArgs() {
loadArgs(0, argumentTypes.length);
}
public void loadArgArray() {
push(argumentTypes.length);
newArray(OBJECT_TYPE);
for (int i = 0; i < argumentTypes.length; i++) {
dup();
push(i);
loadArg(i);
box(argumentTypes[i]);
arrayStore(OBJECT_TYPE);
}
}
public void storeArg(final int arg) {
storeInsn(argumentTypes[arg], getArgIndex(arg));
}
}
```
### 2.3 boxing and unboxing
在`GeneratorAdapter`类当中,定义了一些与boxing和unboxing相关的操作。
```java
public class GeneratorAdapter extends LocalVariablesSorter {
public void box(final Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return;
}
if (type == Type.VOID_TYPE) {
push((String) null);
} else {
Type boxedType = getBoxedType(type);
newInstance(boxedType);
if (type.getSize() == 2) {
dupX2();
dupX2();
pop();
} else {
dupX1();
swap();
}
invokeConstructor(boxedType, new Method("", Type.VOID_TYPE, new Type[] {type}));
}
}
public void unbox(final Type type) {
Type boxedType = NUMBER_TYPE;
Method unboxMethod;
switch (type.getSort()) {
case Type.VOID:
return;
case Type.CHAR:
boxedType = CHARACTER_TYPE;
unboxMethod = CHAR_VALUE;
break;
case Type.BOOLEAN:
boxedType = BOOLEAN_TYPE;
unboxMethod = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
unboxMethod = DOUBLE_VALUE;
break;
case Type.FLOAT:
unboxMethod = FLOAT_VALUE;
break;
case Type.LONG:
unboxMethod = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
unboxMethod = INT_VALUE;
break;
default:
unboxMethod = null;
break;
}
if (unboxMethod == null) {
checkCast(type);
} else {
checkCast(boxedType);
invokeVirtual(boxedType, unboxMethod);
}
}
public void valueOf(final Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return;
}
if (type == Type.VOID_TYPE) {
push((String) null);
} else {
Type boxedType = getBoxedType(type);
invokeStatic(boxedType, new Method("valueOf", boxedType, new Type[] {type}));
}
}
private static Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_TYPE;
case Type.BOOLEAN:
return BOOLEAN_TYPE;
case Type.SHORT:
return SHORT_TYPE;
case Type.CHAR:
return CHARACTER_TYPE;
case Type.INT:
return INTEGER_TYPE;
case Type.FLOAT:
return FLOAT_TYPE;
case Type.LONG:
return LONG_TYPE;
case Type.DOUBLE:
return DOUBLE_TYPE;
default:
return type;
}
}
}
```
## 3. 示例:生成类
### 3.1 预期目标
我们想实现的预期目标:生成一个`HelloWorld`类,代码如下所示。
```java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
```
### 3.2 编码实现
```java
import lsieun.utils.FileUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.util.TraceClassVisitor;
import java.io.PrintStream;
import java.io.PrintWriter;
import static org.objectweb.asm.Opcodes.*;
public class GeneratorAdapterExample01 {
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 {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
PrintWriter printWriter = new PrintWriter(System.out);
TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter);
cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "sample/HelloWorld", null, "java/lang/Object", null);
{
Method m1 = Method.getMethod("void ()");
GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m1, null, null, cv);
mg.loadThis();
mg.invokeConstructor(Type.getType(Object.class), m1);
mg.returnValue();
mg.endMethod();
}
{
Method m2 = Method.getMethod("void main (String[])");
GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m2, null, null, cv);
mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
mg.push("Hello World!");
mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)"));
mg.returnValue();
mg.endMethod();
}
cv.visitEnd();
return cw.toByteArray();
}
}
```
## 4. 总结
本文对`GeneratorAdapter`类进行介绍,内容总结如下:
- 第一点,`GeneratorAdapter`类的特点是将一些`visitXxxInsn()`方法封装成一些常用的方法。
- 第二点,`GeneratorAdapter`类定义的新方法,并不是十分必要的;如果熟悉`MethodVisitor.visitXxxInsn()`方法,可以完全不考虑使用`GeneratorAdapter`类。