Setup label value in constructor to be invalid until 'invoke(Controller)' call

This commit is contained in:
Denis Zharkov
2016-06-03 12:16:42 +03:00
parent 759cb83dfb
commit 678e8c2baa
4 changed files with 71 additions and 4 deletions
@@ -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");