ClassNode信息

类型 名称 说明
int version class文件的major版本(编译的java版本)
int access 访问级
String name 类名,采用全地址,如java/lang/String
String signature 签名,通常是null
String superName 父类类名,采用全地址
List interfaces 实现的接口,采用全地址
String sourceFile 源文件,可能为null
String sourceDebug debug源,可能为null
String outerClass 外部类
String outerMethod 外部方法
String outerMethodDesc 外部方法描述(包括方法参数和返回值)
List visibleAnnotations 可见的注解
List invisibleAnnotations 不可见的注解
List attrs 类的Attribute
List innerClasses 类的内部类列表
List fields 类的字段列表
List methods 类的方法列表

FieldNode信息

类型 名称 说明
int access 访问级
String name 字段名
String signature 签名,通常是 null
String desc 类型描述,例如 Ljava/lang/String、D(double)、F(float)
Object value 初始值,通常为 null
List visibleAnnotations 可见的注解
List invisibleAnnotations 不可见的注解
List attrs 字段的 Attribute

MethodNode信息

类型 名称 说明
int access 访问级
String name 方法名
String desc 方法描述,其包含方法的返回值和参数
String signature 签名,通常是null
List exceptions 可能返回的异常列表
List visibleAnnotations 可见的注解列表
List invisibleAnnotations 不可见的注解列表
List attrs 方法的Attribute列表
Object annotationDefault 默认的注解
List[] visibleParameterAnnotations 可见的参数注解列表
List[] invisibleParameterAnnotations 不可见的参数注解列表
InsnList instructions 操作码列表
List tryCatchBlocks try-catch块列表
int maxStack 最大操作栈的深度
int maxLocals 最大局部变量区的大小
List localVariables 本地(局部)变量节点列表

Instructions指令码信息

名称 说明 参数
FieldInsnNode 用于 GETFIELD 和 PUTFIELD 之类的字段操作的字节码 String owner 字段所在的类
String name 字段的名称
String desc 字段的类型
FrameNode 栈映射帧的对应的帧节点 待补充
IincInsnNode 用于 IINC 变量自加操作的字节码 int var:目标局部变量的位置
int incr: 要增加的数

| | InsnNode | 一切无参数值操作的字节码,例如 ALOAD0,DUP(注意不包含 POP) | 无 | | IntInsnNode | 用于 BIPUSH、SIPUSH 和 NEWARRAY 这三个直接操作整数的操作 | int operand:操作的整数值 | | InvokeDynamicInsnNode | 用于 Java7 新增的 INVOKEDYNAMIC 操作的字节码 | String name:方法名称
String desc:方法描述
Handle bsm:句柄
Object[] bsmArgs:参数常量 | | JumpInsnNode | 用于 IFEQ 或 GOTO 等跳转操作字节码 | LabelNode lable:目标 lable | | LabelNode | 一个用于表示跳转点的 Label 节点 | 无 | | LdcInsnNode | 使用 LDC 加载常量池中的引用值并进行插入的字节码 | Object cst:引用值 | | LineNumberNode | 表示行号的节点 | int line:行号
LabelNode start:对应的第一个 Label | | LookupSwitchInsnNode | 用于实现 LOOKUPSWITCH 操作的字节码 | LabelNode dflt:default 块对应的 Lable
List keys 键列表
List labels:对应的 Label 节点列表 | | MethodInsnNode | 用于 INVOKEVIRTUAL ,_INVOKESTATIC,INVOKESPECIAL

等传统方法调用操作的字节码,不适用于 Java7 新增的 INVOKEDYNAMIC | String owner :方法所在的类
String name :方法名称
String desc:方法描述 | | MultiANewArrayInsnNode | 用于 MULTIANEWARRAY 操作的字节码 | String desc:类型描述
int dims:维数 | | TableSwitchInsnNode | 用于实现 TABLESWITCH 操作的字节码 | int min:键的最小值
int max:键的最大值
LabelNode dflt:default 块对应的 Lable
List labels:对应的 Label 节点列表 | | TypeInsnNode | 用于实现 NEW、ANEWARRAY 和 CHECKCAST 等类型相关操作的字节码 | String desc:类型

| | VarInsnNode | 用于实现 ALOAD、ASTORE 等局部变量操作的字节码 | int var:局部变量 |

指令码操作

添加

  • add(AbstractInsnNode insn)将一个操作码添加到 InsnList 的末尾
  • insert(AbstractInsnNode insn)将一个操作码插入到这个 InsnList 的开头
  • insert(AbstractInsnNode insnNode,AbstractInsnNode insn)将一个操作码插入到另一个操作码的下面
  • insertBefore(AbstractInsnNode insnNode,AbstractInsnNode insn) 将一个操作码插入到另一个操作码的上面

比如要将

  1. thread.setName("helo name");

替换为

  1. thread.setName(ShadowThread.makeThreadName("helo name", "\u200bnet.mikaelzero.myapplication.Test"));

通过javap得出对应的字节码

  1. ldc #6 // String helo name
  2. invokevirtual #7 // Method java/lang/Thread.setName:(Ljava/lang/String;)V
  3. 19 ldc #26 <helo name>
  4. 21 ldc #21 <net.mikaelzero.myapplication.Test>
  5. 23 invokestatic #30 <com/didiglobal/booster/instrument/ShadowThread.makeThreadName>
  6. 26 invokevirtual #36 <java/lang/Thread.setName>

因此我们可以在原字节码操作列表的 invokevirtual 节点的前面插入,比如

  1. method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
  2. method.instructions.insertBefore(this, MethodInsnNode(Opcodes.INVOKESTATIC, SHADOW_THREAD, "setThreadName", "(Ljava/lang/Thread;Ljava/lang/String;)Ljava/lang/Thread;", false))