请先简单阅读下原文代码
https://blog.csdn.net/weixin_44618248/article/details/107086410
可以对照源代码在阅读本文同时可以看得更清晰一些
注⚠️:以下属于个人学习,理解 如果偏颇过深 非常欢迎在评论看到您的看法和想法
先看前一段代码
val startTimeLabel = newLabel() //标签
val endTimeLabel = newLabel() //标签
⬆️ 用于向 本地变量表1⃣️ 中插入变量时候使用
var startTimeIndex: Int = 0
⬆️ 用于记录 变量在 本地变量表1⃣️ 里面的位置
因为待会要从这个 本地变量表1⃣️ 里面去取数据
override fun onMethodEnter() {
super.onMethodEnter()
startTimeIndex = newLocal(Type.DOUBLE_TYPE)
⬆️ 通过 newLocal方法新建一个类型为 Double 的变量并记录它在 本地变量表1⃣️ 中的位置
startTimeLabel.let {
visitLabel(it)
}
⬆️ 记录label的顺序 表示在这一行开始
mv.visitLocalVariable(
"startTime",
"J",
null,
startTimeLabel,
endTimeLabel,
startTimeIndex
)
⬆️ 向 本地变量表1⃣️ 中声明变量 :
- 变量名称叫做startTime
- 类型为 J2⃣️
- 没有泛形
- 变量存活生命周期开始于 startTimeLabel
- 变量存活生命周期结束于 endTimeLabel
- 它的下标位置在 startTimeIndex
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"android/os/SystemClock",
"currentThreadTimeMillis",
"()J",
false
)
⬆️ 在该字节码下一行插入语句:
SystemClock.currentThreadTimeMillis()
对应位置参数:
- Opcodes.INVOKESTATIC -> 表示执行一个类的静态方法
- 类叫做android/os/SystemClock
- 方法名称叫 currentThreadTimeMillis
- 方法的描述符号3⃣️ 为()J
println("插入本地变量表index=${startTimeIndex}")
mv.visitVarInsn(Opcodes.LSTORE, startTimeIndex)
}
⬆️ 把刚刚方法的返回值 存到下标为 startTimeIndex 的本地变量表中
Opcodes.LSTORE 是指令 表示存一个Long类型的变量
为什么这样就存入到 本地变量表?因为方法执行后 返回值还在栈中 可以继续用指令
操作栈中数据
方法即将要执行完毕的时候给他插入一些字节码
override fun onMethodExit(opcode: Int) {
//求时间差 并存入 本地变量表
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"android/os/SystemClock",
"currentThreadTimeMillis",
"()J",
false
)
⬆️ 执行方法 SystemClock.currentThreadTimeMillis()
mv.visitVarInsn(Opcodes.LLOAD, startTimeIndex)
⬆️ 从下标为 startTimeIndex 的 本地变量表1⃣️ 中获取值 并放入栈
mv.visitInsn(Opcodes.LSUB)
⬆️ 栈中元素 进行相减 操作
mv.visitVarInsn(Opcodes.LSTORE, startTimeIndex)
⬆️ 栈中元素 存入到下标为 startTimeIndex 的 本地变量表1⃣️ 中
//StringBuilder 构建str 并存入本地变量表
val strVarIndex = newLocal(Type.getType(String::class.java))
val strBeginLabel = newLabel()
val strEndLabel = newLabel()
mv.visitLocalVariable(
"__tempStr",
"Ljava/lang/String;",
null,
strBeginLabel,
strEndLabel,
strVarIndex
)
strBeginLabel.let {
visitLabel(it)
}
⬆️ 创建变量 __tempStr (前面有过类似 就不在赘述)
//NEW java/lang/StringBuilder
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder")
⬆️ new 一个对象 对象为StringBuilder
// DUP
mv.visitInsn(Opcodes.DUP)
⬆️ 复制栈顶数值并将复制值压入栈顶
// INVOKESPECIAL java/lang/StringBuilder.<init> ()V
mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
"java/lang/StringBuilder",
"<init>",
"()V",
false
)
⬆️ 调用方法 < init >
// LDC "Wtf"
mv.visitLdcInsn(customTAG)
⬆️ 向栈中放入字符串(Wtf) customTAG里面的值(Wtf)
// INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false
)
⬆️ 调用方法 append 相当于 StringBuilder.append(“Wtf”)
// ILOAD 2
mv.visitLdcInsn(",方法${name}消耗时间:")
// INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false
)
⬆️ 继续调用方法 相当于 StringBuilder.append(",方法${name}消耗时间:")
//求得的时间 入栈
mv.visitVarInsn(Opcodes.LLOAD, startTimeIndex)
//append 到 StringBuilder上
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(J)Ljava/lang/StringBuilder;",
false
)
⬆️ 从本地变量表中读取 并执行相当于:
StringBuilder.append(startTime)
// ILOAD 2
mv.visitLdcInsn("毫秒")
// INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false
)
⬆️ 继续调用方法 相当于 StringBuilder.append(“毫秒”)
//toString
// INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder",
"toString",
"()Ljava/lang/String;",
false
)
⬆️ 继续调用方法 相当于 StringBuilder.toString()
mv.visitVarInsn(Opcodes.ASTORE, strVarIndex)
⬆️ 栈中值 存入到 下标为 strVarIndex的变量中
mv.visitFieldInsn(
Opcodes.GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;"
)
mv.visitVarInsn(Opcodes.ALOAD, strVarIndex)
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
"(Ljava/lang/String;)V",
false
)
⬆️ 继续调用方法 相当于 System.out.println()
endTimeLabel.let {
visitLabel(it)
}
strEndLabel.let {
visitLabel(it)
}
super.onMethodExit(opcode)
}
本地变量表
此图是我用javap 命令(javap -c -v xxx.class)获取到的(编译的时候要加额外参数 javac -g xxx.java)
本地变量表是方法内变量的集合 例如实例方法中的this 你可以直接用 是因为 方法默认给你
设置了一个 静态方法就没有 所以你也能明白了吧 在一个类中 它的静态方法为什么不可以调用它的实例方法
字节码中的变量类型
java代码中 | 字节码中 |
---|---|
boolean | Z |
int | I |
long | J |
float | F |
double | D |
对象类型 | L+全类名; |
[] | [+对应类型 |
方法描述符号
规则 ()+返回值
eg: ()V 表示 无入参 且无返回值
(I)V ➡️ fun(Int)
(Ljava/lang/String;)Ljava/lang/String; ➡️ fun(String):String
特殊方法
- < init > : 构造方法