ASM Opcodes 完整属性详解
ASM Opcodes 完整属性详解
Opcodes 类概述
Opcodes 类是 ASM 库中的常量定义类,包含了所有 Java 字节码操作码、访问标志、版本号等常量。这些常量用于在字节码级别操作和生成 Java 类。
1. Java 版本常量
常量 | 值 | 描述 | 对应 Java 版本 |
|---|---|---|---|
| 45 | Java 1.1 版本号 | Java 1.1 |
| 46 | Java 1.2 版本号 | Java 1.2 |
| 47 | Java 1.3 版本号 | Java 1.3 |
| 48 | Java 1.4 版本号 | Java 1.4 |
| 49 | Java 5 版本号 | Java 5 |
| 50 | Java 6 版本号 | Java 6 |
| 51 | Java 7 版本号 | Java 7 |
| 52 | Java 8 版本号 | Java 8 |
| 53 | Java 9 版本号 | Java 9 |
| 54 | Java 10 版本号 | Java 10 |
| 55 | Java 11 版本号 | Java 11 |
| 56 | Java 12 版本号 | Java 12 |
| 57 | Java 13 版本号 | Java 13 |
| 58 | Java 14 版本号 | Java 14 |
| 59 | Java 15 版本号 | Java 15 |
| 60 | Java 16 版本号 | Java 16 |
| 61 | Java 17 版本号 | Java 17 |
| 62 | Java 18 版本号 | Java 18 |
| 63 | Java 19 版本号 | Java 19 |
| 64 | Java 20 版本号 | Java 20 |
| 65 | Java 21 版本号 | Java 21 |
使用示例:
// 定义不同版本的类
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);// Java 8 类
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "com/example/Java8Class", null, "java/lang/Object", null);// Java 11 类
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "com/example/Java11Class", null, "java/lang/Object", null);2. 访问修饰符常量
类访问修饰符
常量 | 值 | 描述 |
|---|---|---|
| 0x0001 | public 访问权限 |
| 0x0010 | final 类,不可被继承 |
| 0x0020 | 使用新的 invokespecial 语义 |
| 0x0200 | 接口类型 |
| 0x0400 | 抽象类 |
| 0x1000 | 合成类,由编译器生成 |
| 0x2000 | 注解类型 |
| 0x4000 | 枚举类型 |
| 0x8000 | 模块(Java 9+) |
使用示例:
// public final 类
int classAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL;
cw.visit(Opcodes.V1_8, classAccess, "com/example/FinalClass", null, "java/lang/Object", null);// 接口定义
int interfaceAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT;
cw.visit(Opcodes.V1_8, interfaceAccess, "com/example/MyInterface", null, "java/lang/Object", null);// 枚举定义
int enumAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_ENUM;
cw.visit(Opcodes.V1_8, enumAccess, "com/example/MyEnum", null, "java/lang/Enum", null);字段访问修饰符
常量 | 值 | 描述 |
|---|---|---|
| 0x0001 | public 字段 |
| 0x0002 | private 字段 |
| 0x0004 | protected 字段 |
| 0x0008 | static 字段 |
| 0x0010 | final 字段 |
| 0x0040 | volatile 字段 |
| 0x0080 | transient 字段 |
| 0x1000 | 合成字段 |
| 0x4000 | 枚举字段 |
使用示例:
// public static final 字段
int fieldAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
cw.visitField(fieldAccess, "CONSTANT", "I", null, 100);// private volatile 字段
int volatileField = Opcodes.ACC_PRIVATE | Opcodes.ACC_VOLATILE;
cw.visitField(volatileField, "counter", "I", null, null);方法访问修饰符
常量 | 值 | 描述 |
|---|---|---|
| 0x0001 | public 方法 |
| 0x0002 | private 方法 |
| 0x0004 | protected 方法 |
| 0x0008 | static 方法 |
| 0x0010 | final 方法 |
| 0x0020 | synchronized 方法 |
| 0x0040 | 桥接方法(编译器生成) |
| 0x0080 | 可变参数方法 |
| 0x0100 | native 方法 |
| 0x0400 | 抽象方法 |
| 0x0800 | strictfp 方法 |
| 0x1000 | 合成方法 |
使用示例:
// public static 方法
int methodAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
MethodVisitor mv = cw.visitMethod(methodAccess, "staticMethod", "()V", null, null);// synchronized 方法
int syncMethod = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNCHRONIZED;
MethodVisitor mv2 = cw.visitMethod(syncMethod, "syncMethod", "()V", null, null);// native 方法
int nativeMethod = Opcodes.ACC_PUBLIC | Opcodes.ACC_NATIVE;
MethodVisitor mv3 = cw.visitMethod(nativeMethod, "nativeMethod", "()V", null, null);3. 栈操作指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 0 | 空操作 |
|
| 1 | 将 null 压入栈 |
|
| 2 | 将 int -1 压入栈 |
|
| 3 | 将 int 0 压入栈 |
|
| 4 | 将 int 1 压入栈 |
|
| 5 | 将 int 2 压入栈 |
|
| 6 | 将 int 3 压入栈 |
|
| 7 | 将 int 4 压入栈 |
|
| 8 | 将 int 5 压入栈 |
|
| 9 | 将 long 0 压入栈 |
|
| 10 | 将 long 1 压入栈 |
|
| 11 | 将 float 0 压入栈 |
|
| 12 | 将 float 1 压入栈 |
|
| 13 | 将 float 2 压入栈 |
|
| 14 | 将 double 0 压入栈 |
|
| 15 | 将 double 1 压入栈 |
|
使用示例:
// 将常量压入栈
mv.visitInsn(Opcodes.ICONST_0); // 压入 0
mv.visitInsn(Opcodes.ICONST_1); // 压入 1
mv.visitInsn(Opcodes.ACONST_NULL); // 压入 null// 创建数组时常用
mv.visitInsn(Opcodes.ICONST_3); // 压入 3,用于创建长度为3的数组
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");4. 局部变量加载指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 21 | 加载 int 局部变量 |
|
| 22 | 加载 long 局部变量 |
|
| 23 | 加载 float 局部变量 |
|
| 24 | 加载 double 局部变量 |
|
| 25 | 加载引用类型局部变量 |
|
| 26 | 加载第0个 int 局部变量 |
|
| 27 | 加载第1个 int 局部变量 |
|
| 28 | 加载第2个 int 局部变量 |
|
| 29 | 加载第3个 int 局部变量 |
|
| 42 | 加载第0个引用局部变量(通常是this) |
|
使用示例:
// 加载局部变量
mv.visitVarInsn(Opcodes.ALOAD, 0); // 加载 this
mv.visitVarInsn(Opcodes.ILOAD, 1); // 加载第一个int参数
mv.visitVarInsn(Opcodes.ALOAD, 2); // 加载第二个引用参数// 优化指令(索引0-3)
mv.visitVarInsn(Opcodes.ALOAD_0); // 等同于 aload 0
mv.visitVarInsn(Opcodes.ILOAD_1); // 等同于 iload 15. 局部变量存储指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 54 | 存储 int 到局部变量 |
|
| 55 | 存储 long 到局部变量 |
|
| 56 | 存储 float 到局部变量 |
|
| 57 | 存储 double 到局部变量 |
|
| 58 | 存储引用到局部变量 |
|
| 59 | 存储到第0个 int 局部变量 |
|
| 60 | 存储到第1个 int 局部变量 |
|
使用示例:
// 计算并存储结果
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitInsn(Opcodes.IADD); // 相加
mv.visitVarInsn(Opcodes.ISTORE, 3); // 存储到局部变量3// 优化指令
mv.visitVarInsn(Opcodes.ISTORE_1); // 存储到局部变量16. 数组操作指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 46 | 加载 int 数组元素 |
|
| 51 | 加载 byte 数组元素 |
|
| 50 | 加载引用数组元素 |
|
| 79 | 存储到 int 数组 |
|
| 84 | 存储到 byte 数组 |
|
| 83 | 存储到引用数组 |
|
| 190 | 获取数组长度 |
|
使用示例:
// 创建并操作数组
mv.visitInsn(Opcodes.ICONST_3);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");// 存储到数组
mv.visitInsn(Opcodes.DUP); // 复制数组引用
mv.visitInsn(Opcodes.ICONST_0); // 索引0
mv.visitLdcInsn("Hello"); // 值
mv.visitInsn(Opcodes.AASTORE); // 存储// 加载数组元素
mv.visitVarInsn(Opcodes.ALOAD, 1); // 加载数组引用
mv.visitInsn(Opcodes.ICONST_0); // 索引0
mv.visitInsn(Opcodes.AALOAD); // 加载元素7. 算术运算指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 96 | int 加法 |
|
| 100 | int 减法 |
|
| 104 | int 乘法 |
|
| 108 | int 除法 |
|
| 112 | int 取模 |
|
| 116 | int 取负 |
|
| 97 | long 加法 |
|
| 98 | float 加法 |
|
| 99 | double 加法 |
|
使用示例:
// 算术运算
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitInsn(Opcodes.IADD); // a + bmv.visitVarInsn(Opcodes.ILOAD, 3);
mv.visitInsn(Opcodes.ISUB); // (a + b) - cmv.visitVarInsn(Opcodes.ILOAD, 4);
mv.visitInsn(Opcodes.IMUL); // ((a + b) - c) * d8. 类型转换指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 133 | int 转 long |
|
| 134 | int 转 float |
|
| 135 | int 转 double |
|
| 136 | long 转 int |
|
| 139 | float 转 int |
|
| 142 | double 转 int |
|
| 145 | int 转 byte |
|
| 146 | int 转 char |
|
| 147 | int 转 short |
|
使用示例:
// 类型转换
mv.visitVarInsn(Opcodes.ILOAD, 1); // 加载 int
mv.visitInsn(Opcodes.I2L); // 转换为 long
mv.visitVarInsn(Opcodes.ILOAD, 2); // 加载另一个 int
mv.visitInsn(Opcodes.I2L); // 转换为 long
mv.visitInsn(Opcodes.LADD); // long 加法// 截断转换
mv.visitVarInsn(Opcodes.DLOAD, 1); // 加载 double
mv.visitInsn(Opcodes.D2I); // 转换为 int(截断)9. 对象操作指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 187 | 创建新对象 |
|
| 188 | 创建基本类型数组 |
|
| 189 | 创建引用类型数组 |
|
| 197 | 创建多维数组 |
|
| 192 | 类型检查转换 |
|
| 193 | 实例类型检查 |
|
| 180 | 获取实例字段值 |
|
| 181 | 设置实例字段值 |
|
| 178 | 获取静态字段值 |
|
| 179 | 设置静态字段值 |
|
使用示例:
// 创建对象
mv.visitTypeInsn(Opcodes.NEW, "java/util/Date");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/Date", "<init>", "()V", false);// 字段操作
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, "com/example/MyClass", "name", "Ljava/lang/String;");// 类型检查
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/String");
mv.visitTypeInsn(Opcodes.INSTANCEOF, "java/util/List");10. 方法调用指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 182 | 调用实例方法(虚方法) |
|
| 183 | 调用特殊方法(构造、私有、父类) |
|
| 184 | 调用静态方法 |
|
| 185 | 调用接口方法 |
|
| 186 | 调用动态方法(Java 7+) |
|
使用示例:
// 调用实例方法
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);// 调用构造函数
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);// 调用静态方法
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Math", "max", "(II)I", false);// 调用接口方法
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List", "size", "()I", true);11. 控制转移指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 153 | 如果等于0则跳转 |
|
| 154 | 如果不等于0则跳转 |
|
| 155 | 如果小于0则跳转 |
|
| 156 | 如果大于等于0则跳转 |
|
| 157 | 如果大于0则跳转 |
|
| 158 | 如果小于等于0则跳转 |
|
| 159 | 如果两个int相等则跳转 |
|
| 160 | 如果两个int不相等则跳转 |
|
| 167 | 无条件跳转 |
|
| 168 | 跳转到子程序 |
|
| 169 | 从子程序返回 |
|
| 170 | switch 表跳转 |
|
| 171 | switch 查找跳转 |
|
使用示例:
Label start = new Label();
Label end = new Label();
Label ifTrue = new Label();mv.visitLabel(start);
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitJumpInsn(Opcodes.IFNE, ifTrue); // 如果不等于0跳转// false 分支代码
mv.visitInsn(Opcodes.ICONST_0);
mv.visitJumpInsn(Opcodes.GOTO, end);// true 分支代码
mv.visitLabel(ifTrue);
mv.visitInsn(Opcodes.ICONST_1);// 结束标签
mv.visitLabel(end);12. 返回指令
常量 | 值 | 描述 | 示例 |
|---|---|---|---|
| 172 | 返回 int |
|
| 173 | 返回 long |
|
| 174 | 返回 float |
|
| 175 | 返回 double |
|
| 176 | 返回引用 |
|
| 177 | 返回 void |
|
使用示例:
// 根据不同返回类型使用不同指令
Type returnType = Type.getReturnType(methodDesc);
switch (returnType.getSort()) {case Type.VOID:mv.visitInsn(Opcodes.RETURN);break;case Type.INT:mv.visitInsn(Opcodes.IRETURN);break;case Type.OBJECT:mv.visitInsn(Opcodes.ARETURN);break;// ... 其他类型
}13. ASM 特定常量
常量 | 值 | 描述 |
|---|---|---|
| 4 << 16 | ASM 4 API 版本 |
| 5 << 16 | ASM 5 API 版本 |
| 6 << 16 | ASM 6 API 版本 |
| 7 << 16 | ASM 7 API 版本 |
| 8 << 16 | ASM 8 API 版本 |
| 9 << 16 | ASM 9 API 版本 |
使用示例:
// 使用特定版本的 ASM API
ClassVisitor cv = new ClassVisitor(Opcodes.ASM7) {// 类访问逻辑
};MethodVisitor mv = new MethodVisitor(Opcodes.ASM7) {// 方法访问逻辑
};14. 其他重要常量
常量 | 值 | 描述 |
|---|---|---|
| 1 | 句柄类型:GETFIELD |
| 2 | 句柄类型:GETSTATIC |
| 3 | 句柄类型:PUTFIELD |
| 4 | 句柄类型:PUTSTATIC |
| 5 | 句柄类型:INVOKEVIRTUAL |
| 6 | 句柄 |
