This is a dummy code to explain the issue;
StringWrapperNote: The StringWrapper is an inline class.
@JvmInline
value class StringWrapper(val value: String)
class DummyServiceWrapper {
private val p = DummyPrinter()
suspend fun printHello() {
p.printHello2()
}
suspend fun printHello1() {
val value = p.printHello1()
print("hey there $value")
}
suspend fun printHello2(): String {
return p.printHello2()
}
suspend fun printHello3(): StringWrapper {
return p.printHello1()
}
suspend fun printHello4(): StringWrapper {
p.printHello1()
return StringWrapper("Hey there")
}
}
Below is the generated code for the above class;
public final class DummyServiceWrapper {
private final DummyPrinter p = new DummyPrinter();
@Nullable
public final Object printHello(@NotNull Continuation $completion) {
Object var10000 = this.p.printHello2($completion);
return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : Unit.INSTANCE;
}
@Nullable
public final Object printHello1(@NotNull Continuation var1) {
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return DummyServiceWrapper.this.printHello1(this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch (((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
DummyPrinter var7 = this.p;
((<undefinedtype>)$continuation).label = 1;
var10000 = var7.printHello1-og2vJxQ((Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = ((StringWrapper)$result).unbox-impl();
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String value = (String)var10000;
String var3 = "hey there " + StringWrapper.toString-impl(value);
System.out.print(var3);
return Unit.INSTANCE;
}
@Nullable
public final Object printHello2_og2vJxQ/* $FF was: printHello2-og2vJxQ*/(@NotNull Continuation var1) {
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
Object var10000 = DummyServiceWrapper.this.printHello2-og2vJxQ(this);
return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : StringWrapper.box-impl((String)var10000);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch (((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
DummyPrinter var5 = this.p;
((<undefinedtype>)$continuation).label = 1;
var10000 = var5.printHello1-og2vJxQ((Continuation)$continuation);
if (var10000 == var4) {
return var4;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = ((StringWrapper)$result).unbox-impl();
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return (String)var10000;
}
@Nullable
public final Object printHello3_og2vJxQ/* $FF was: printHello3-og2vJxQ*/(@NotNull Continuation var1) {
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
Object var10000 = DummyServiceWrapper.this.printHello3-og2vJxQ(this);
return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : StringWrapper.box-impl((String)var10000);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch (((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
DummyPrinter var5 = this.p;
((<undefinedtype>)$continuation).label = 1;
var10000 = var5.printHello1-og2vJxQ((Continuation)$continuation);
if (var10000 == var4) {
return var4;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = ((StringWrapper)$result).unbox-impl();
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String var6 = (String)var10000;
return StringWrapper.constructor-impl("Hey there");
}
}
Can someone please explain why it has an extra branching inside the invokeSuspend
Given below,
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
Object var10000 = DummyServiceWrapper.this.printHello3-og2vJxQ(this);
return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 :
StringWrapper.box-impl((String)var10000);
}
Compared to this when the suspending call does not return a inline type;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return DummyServiceWrapper.this.printHello1(this);
}
When a suspend function is compiled, the compiler will add a Continuation type parameter and change the return type to Object.
Continuation parameter : To resume at where a function suspended. In Continuation, there is a label property to mark a resume point.Object return type : When a suspend function suspends, COROUTINE_SUSPENDED is returned. its type is Any, which is Object in Java.And according to the inline value classes document, inline classes are boxed whenever they are used as another type.
In StringWrapper, there are constructor_impl and box_impl.
constructor_impl : Return the parameter as the value type(String), called whenever you create a StringWrapper instance. StringWrapper("string") in Kotlin is StringWrapper.constructor_impl("string") in Java.box_impl : Box the parameter into StringWrapper class. Called when it is needed.In printHello4, return StringWrapper("Hey there") in Kotlin will be return StringWrapper.constructor_impl("Hey There") in Java, and the return value is String. But it need to be boxed as it is represented as Object. So it is boxed in invokeSuspend.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With