Fix problem with transforming suspend lambda instantiation by itself

#KT-21605: Fixed
This commit is contained in:
Ilmir Usmanov
2018-01-10 20:07:16 +03:00
parent f3bc8d179c
commit e169383f76
6 changed files with 172 additions and 2 deletions
@@ -282,11 +282,26 @@ class MethodInliner(
info = oldInfo
}
val isContinuationCreate = isContinuation && oldInfo != null && resultNode.name == "create" &&
resultNode.desc.startsWith("(" + CONTINUATION_ASM_TYPE.descriptor)
for (capturedParamDesc in info.allRecapturedParameters) {
visitFieldInsn(
if (capturedParamDesc.fieldName == THIS && isContinuationCreate) {
// Common inliner logic doesn't support cases when transforming anonymous object can
// be instantiated by itself.
// To support such cases workaround with 'oldInfo' is used.
// But it corresponds to outer context and a bit inapplicable for nested 'create' method context.
// 'This' in outer context corresponds to outer instance in current
visitFieldInsn(
Opcodes.GETSTATIC, owner,
CAPTURED_FIELD_FOLD_PREFIX + THIS_0, capturedParamDesc.type.descriptor
)
} else {
visitFieldInsn(
Opcodes.GETSTATIC, capturedParamDesc.containingLambdaName,
CAPTURED_FIELD_FOLD_PREFIX + capturedParamDesc.fieldName, capturedParamDesc.type.descriptor
)
)
}
}
super.visitMethodInsn(opcode, info.newClassName, name, info.newConstructorDescriptor, itf)
@@ -0,0 +1,131 @@
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
interface Consumer { fun consume(s: String) }
inline fun crossInlineBuilderConsumer(crossinline block: (String) -> Unit) = object : Consumer {
override fun consume(s: String) {
block(s)
}
}
inline fun inlineBuilder(block: () -> Consumer) = block()
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun builderConsumer(c: suspend () -> Consumer): Consumer {
var res: Consumer? = null
c.startCoroutine(object : Continuation<Consumer> {
override fun resume(value: Consumer) {
res = value
}
override fun resumeWithException(e: Throwable) {
throw e
}
override val context = EmptyCoroutineContext
})
return res!!
}
class Container {
var y: String = "FAIL 1"
val consumer1 = crossInlineBuilderConsumer { s ->
builder {
y = s
}
}
val consumer2 = inlineBuilder {
object : Consumer {
override fun consume(s: String) {
builder {
y = s
}
}
}
}
val consumer3 = inlineBuilder {
builderConsumer {
object : Consumer {
override fun consume(s: String) {
y = s
}
}
}
}
val consumer4 = crossInlineBuilderConsumer { s ->
object : Consumer {
override fun consume(s1: String) {
builder {
y = s1
}
}
}
}
val consumer5 = crossInlineBuilderConsumer { s ->
builderConsumer {
object : Consumer {
override fun consume(s1: String) {
y = s1
}
}
}
}
val consumer6 = crossInlineBuilderConsumer { s ->
val c = object : Consumer {
override fun consume(s1: String) {
builder {
y = s1
}
}
}
c.consume(s)
}
val consumer7 = crossInlineBuilderConsumer { s ->
val c = builderConsumer {
object : Consumer {
override fun consume(s1: String) {
y = s1
}
}
}
c.consume(s)
}
}
fun box(): String {
val c = Container()
c.consumer1.consume("OK")
if (c.y != "OK") return c.y
c.y = "FAIL 2"
c.consumer2.consume("OK")
if (c.y != "OK") return c.y
c.y = "FAIL 3"
c.consumer3.consume("OK")
if (c.y != "OK") return c.y
c.y = "OK"
c.consumer4.consume("FAIL 4")
if (c.y != "OK") return c.y
c.consumer5.consume("FAIL 5")
if (c.y != "OK") return c.y
c.y = "FAIL 6"
c.consumer6.consume("OK")
if (c.y != "OK") return c.y
c.y = "FAIL 7"
c.consumer7.consume("OK")
if (c.y != "OK") return c.y
return "OK"
}
@@ -5787,6 +5787,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("crossInlineWithCapturedOuterReceiver.kt")
public void testCrossInlineWithCapturedOuterReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt");
doTest(fileName);
}
@TestMetadata("defaultParametersInSuspend.kt")
public void testDefaultParametersInSuspend() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
@@ -5787,6 +5787,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("crossInlineWithCapturedOuterReceiver.kt")
public void testCrossInlineWithCapturedOuterReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt");
doTest(fileName);
}
@TestMetadata("defaultParametersInSuspend.kt")
public void testDefaultParametersInSuspend() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
@@ -5787,6 +5787,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("crossInlineWithCapturedOuterReceiver.kt")
public void testCrossInlineWithCapturedOuterReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt");
doTest(fileName);
}
@TestMetadata("defaultParametersInSuspend.kt")
public void testDefaultParametersInSuspend() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
@@ -6387,6 +6387,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
}
@TestMetadata("crossInlineWithCapturedOuterReceiver.kt")
public void testCrossInlineWithCapturedOuterReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt");
doTest(fileName);
}
@TestMetadata("defaultParametersInSuspend.kt")
public void testDefaultParametersInSuspend() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");