让我们考虑一个简单的函数:
fun main() {
greet("world")
}
fun greet(name: String?) {
if (name != null) {
println("Hello $name!")
} else {
println("Hello guest!")
}
}
如果我使用 D8 将其转换为 dex,字节码将如下:
[000240] DemoKt.main:()V
0000: const-string v0, "world"
0002: invoke-static {v0}, LDemoKt;.greet:(Ljava/lang/String;)V
0005: return-void
[0001dc] DemoKt.greet:(Ljava/lang/String;)V
0000: if-eqz v2, 0021 // +0021
0002: new-instance v0, Ljava/lang/StringBuilder;
0004: invoke-direct {v0}, Ljava/lang/StringBuilder;.<init>:()V
0007: const-string v1, "Hello "
0009: invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
000c: move-result-object v0
000d: invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
0010: move-result-object v0
0011: const/16 v1, #int 33 // #21
0013: invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(C)Ljava/lang/StringBuilder;
0016: move-result-object v0
0017: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String;
001a: move-result-object v0
001b: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
001d: invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V
0020: goto 0028 // +0008
0021: const-string v0, "Hello guest!"
0023: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
0025: invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V
0028: return-void
我可以看到“guest”常量字符串被加载到寄存器v0中,但是greet函数使用v2进行操作。这是为什么?是否有某种寄存器重新映射?
方法的 N 个参数按顺序放置在方法调用帧的 LAST N 个寄存器中。
https://source.android.com/docs/core/runtime/dalvik-bytecode
您的反汇编输出并未说明为“greet”方法分配了多少个寄存器,但根据结果,必须有三个 - v0、v1 和 v2。
每个堆栈帧都有自己的寄存器;一帧的这些寄存器的内容不会影响其他帧中发生的情况。换句话说,“world”保留在 v0 中的“main”方法中,但放置在 v2 中的“greet”方法中,因为这就是它的工作方式。