https://blog.csdn.net/weixin_44618248/article/details/107086410 可以对照源代码在阅读本文同时可以看得更清晰一些
⬆️ 用于向 本地变量表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类型的变量 为什么这样就存入到 本地变量表?因为方法执行后 返回值还在栈中 可以继续用指令 操作栈中数据
⬆️ 执行方法 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 你可以直接用 是因为 方法默认给你 设置了一个 静态方法就没有 所以你也能明白了吧 在一个类中 它的静态方法为什么不可以调用它的实例方法
规则 ()+返回值 eg: ()V 表示 无入参 且无返回值 (I)V ➡️ fun(Int) (Ljava/lang/String;)Ljava/lang/String; ➡️ fun(String):String