在查看由suspend函数的字节码时,我纠结于一个问题。先放kotlin代码:
suspend fun test(){
delay(10000)
print("hello")
}
复制代码
对应的字节码经过反编译为:
public static final Object test(@NotNull Continuation var0) {
Object $continuation;
label20: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
// 创建与test函数相对应的Continuation对象
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestKt.test(this);
}
};
}
// 随即获取其中的result字段值
Object $result = ((<undefinedtype>)$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result); // ???
((<undefinedtype>)$continuation).label = 1;
if (DelayKt.delay(10000L, (Continuation)$continuation) == var4) {
return var4;
}
break;
case 1:
...
break;
default:
...
}
...
return Unit.INSTANCE;
}
复制代码
而我正是纠结于 case 0 分支的逻辑。现在只讨论初次运行test函数。此时传入的参数是完成test函数时,所要回调的函数它所对应的Continuation对象,所以会创建与test函数相相应的Continuation对象。根据执行流程,随即就获取该对象内的result字段的值,而其未被赋予正式的值,也就是说此时存储的是null,同样label也是默认值,即0。显而易见,此时将进入case 0 分支。矛盾来了,众所周知,ResultKt.throwOnFailure($result) 的参数不能为null,而此时传入的正是null,这令我百思不得其解,望众多网友能够帮我解惑。