Setup label value in constructor to be invalid until 'invoke(Controller)' call
This commit is contained in:
@@ -405,6 +405,8 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "()V", false);
|
||||
}
|
||||
|
||||
generateAdditionalCodeInConstructor(iv);
|
||||
|
||||
iv.visitInsn(RETURN);
|
||||
|
||||
FunctionCodegen.endVisit(iv, "constructor", element);
|
||||
@@ -412,6 +414,10 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
protected void generateAdditionalCodeInConstructor(@NotNull InstructionAdapter iv) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<FieldInfo> calculateConstructorParameters(
|
||||
@NotNull KotlinTypeMapper typeMapper,
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
|
||||
class CoroutineCodegen(
|
||||
@@ -73,9 +74,7 @@ class CoroutineCodegen(
|
||||
1, codegen.v)
|
||||
|
||||
with(codegen.v) {
|
||||
load(0, AsmTypes.OBJECT_TYPE)
|
||||
iconst(0)
|
||||
putfield(v.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor)
|
||||
setLabelValue(LABEL_VALUE_BEFORE_FIRST_SUSPENSION)
|
||||
load(0, AsmTypes.OBJECT_TYPE)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
@@ -123,7 +122,6 @@ class CoroutineCodegen(
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
private fun createSynthesizedImplementationByName(
|
||||
name: String,
|
||||
interfaceSupertype: KotlinType,
|
||||
@@ -164,8 +162,23 @@ class CoroutineCodegen(
|
||||
})
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.setLabelValue(value: Int) {
|
||||
load(0, AsmTypes.OBJECT_TYPE)
|
||||
iconst(value)
|
||||
putfield(v.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor)
|
||||
}
|
||||
|
||||
override fun generateAdditionalCodeInConstructor(iv: InstructionAdapter) {
|
||||
super.generateAdditionalCodeInConstructor(iv)
|
||||
// Change label value to illegal to make sure continuation will not be used until `invoke(Controler)`
|
||||
// where correct value will be set up
|
||||
iv.setLabelValue(LABEL_VALUE_BEFORE_INVOKE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LABEL_VALUE_BEFORE_INVOKE = -2
|
||||
private const val LABEL_VALUE_BEFORE_FIRST_SUSPENSION = 0
|
||||
|
||||
@JvmStatic fun create(
|
||||
expressionCodegen: ExpressionCodegen,
|
||||
originalCoroutineLambdaDescriptor: FunctionDescriptor,
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder1(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
(c as Continuation<Unit>).resume(Unit)
|
||||
}
|
||||
|
||||
fun builder2(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
val continuation = c(Controller())
|
||||
val declaredField = continuation!!.javaClass.getDeclaredField("label")
|
||||
declaredField.setAccessible(true)
|
||||
declaredField.set(continuation, -3)
|
||||
continuation.resume(Unit)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
try {
|
||||
builder1 {
|
||||
suspendHere()
|
||||
}
|
||||
return "fail 1"
|
||||
} catch (e: java.lang.IllegalStateException) {
|
||||
if (e.message != "call to 'resume' before 'invoke' with coroutine") return "fail 2: ${e.message!!}"
|
||||
}
|
||||
|
||||
try {
|
||||
builder2 {
|
||||
suspendHere()
|
||||
}
|
||||
return "fail 3"
|
||||
} catch (e: java.lang.IllegalStateException) {
|
||||
if (e.message != "call to 'resume' before 'invoke' with coroutine") return "fail 4: ${e.message!!}"
|
||||
return "OK"
|
||||
}
|
||||
|
||||
return "fail"
|
||||
}
|
||||
@@ -4123,6 +4123,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("illegalState.kt")
|
||||
public void testIllegalState() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/illegalState.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("innerSuspensionCalls.kt")
|
||||
public void testInnerSuspensionCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/innerSuspensionCalls.kt");
|
||||
|
||||
Reference in New Issue
Block a user