Keep DebugMetadata annotation when regenerate lambda with state-machine

When a suspend lambda does not capture crossinline lambda, it is
generated with as state-machine, since it does not inline anything.
However, when regenerating, the inliner used to remove all DebugMetadata
annotations to avoid duplication. This lead to missing annotation if
the lambda is regenerated, but state-machine is not regenerated.
This change fixes the blind spot by readding the annotation after
regeneration.
 #KT-41789 Fixed
This commit is contained in:
Ilmir Usmanov
2020-09-15 03:42:53 +02:00
parent 12489ef1b4
commit 58146c4452
9 changed files with 90 additions and 4 deletions
@@ -57,6 +57,7 @@ class AnonymousObjectTransformer(
lateinit var superClassName: String
var sourceInfo: String? = null
var debugInfo: String? = null
var debugMetadataAnnotation: AnnotationNode? = null
createClassReader().accept(object : ClassVisitor(Opcodes.API_VERSION, classBuilder.visitor) {
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
@@ -77,7 +78,8 @@ class AnonymousObjectTransformer(
val innerClassesInfo = FileBasedKotlinClass.InnerClassesInfo()
return FileBasedKotlinClass.convertAnnotationVisitor(metadataReader, desc, innerClassesInfo)
} else if (desc == DEBUG_METADATA_ANNOTATION_ASM_TYPE.descriptor) {
return null
debugMetadataAnnotation = AnnotationNode(desc)
return debugMetadataAnnotation
}
return super.visitAnnotation(desc, visible)
}
@@ -137,12 +139,20 @@ class AnonymousObjectTransformer(
methodsToTransform,
superClassName
)
var putDebugMetadata = false
loop@ for (next in methodsToTransform) {
val deferringVisitor =
when {
coroutineTransformer.shouldSkip(next) -> continue@loop
coroutineTransformer.shouldGenerateStateMachine(next) -> coroutineTransformer.newMethod(next)
else -> newMethod(classBuilder, next)
else -> {
// Debug metadata is not put, but we should keep, since we do not generate state-machine,
// if the lambda does not capture crossinline lambdas.
if (coroutineTransformer.suspendLambdaWithGeneratedStateMachine(next)) {
putDebugMetadata = true
}
newMethod(classBuilder, next)
}
}
if (next.name == "<clinit>") {
@@ -190,6 +200,13 @@ class AnonymousObjectTransformer(
writeTransformedMetadata(header, classBuilder)
}
// debugMetadataAnnotation can be null in LV < 1.3
if (putDebugMetadata && debugMetadataAnnotation != null) {
visitor.visitAnnotation(debugMetadataAnnotation!!.desc, true).also {
debugMetadataAnnotation!!.accept(it)
}
}
writeOuterInfo(visitor)
if (inliningContext.generateAssertField && fieldNames.none { it.key == ASSERTIONS_DISABLED_FIELD_NAME }) {
@@ -43,11 +43,14 @@ class CoroutineTransformer(
fun shouldGenerateStateMachine(node: MethodNode): Boolean {
// Continuations are similar to lambdas from bird's view, but we should never generate state machine for them
if (isContinuationNotLambda()) return false
// there can be suspend lambdas inside inline functions, which do not
// capture crossinline lambdas, thus, there is no need to transform them
return isSuspendFunctionWithFakeConstructorCall(node) || (isSuspendLambda(node) && !isStateMachine(node))
}
// there can be suspend lambdas inside inline functions, which do not
// capture crossinline lambdas, thus, there is no need to transform them
fun suspendLambdaWithGeneratedStateMachine(node: MethodNode): Boolean =
!isContinuationNotLambda() && isSuspendLambda(node) && isStateMachine(node)
private fun isContinuationNotLambda(): Boolean = inliningContext.isContinuation &&
if (state.languageVersionSettings.isReleaseCoroutines()) superClassName.endsWith("ContinuationImpl")
else methods.any { it.name == "getLabel" }
@@ -0,0 +1,36 @@
// WITH_RUNTIME
// TARGET_BACKEND: JVM
// FILE: inline.kt
package test
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
fun runBlocking(c: suspend () -> Unit) {
c.startCoroutine(Continuation(EmptyCoroutineContext){})
}
inline fun <reified E> foo(noinline block: (String) -> Unit) = runBlocking {
runCatching {
runBlocking {
var c: Continuation<Unit>? = null
suspendCoroutineUninterceptedOrReturn<Unit> { c = it; Unit }
block(c!!.toString())
}
}
Unit
}
// FILE: test.kt
import test.*
fun box(): String {
var res = "FAIL"
foo<Exception> {
res = it
}
return if (res.contains(".invokeSuspend(inline.kt:")) "OK" else res
}
@@ -3818,6 +3818,11 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("debugMetadataCrossinline.kt")
public void testDebugMetadataCrossinline() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/debugMetadataCrossinline.kt");
}
@TestMetadata("delegatedProperties.kt")
public void testDelegatedProperties_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines.experimental");
@@ -3818,6 +3818,11 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("debugMetadataCrossinline.kt")
public void testDebugMetadataCrossinline() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/debugMetadataCrossinline.kt");
}
@TestMetadata("delegatedProperties.kt")
public void testDelegatedProperties_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines.experimental");
@@ -3808,6 +3808,11 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("debugMetadataCrossinline.kt")
public void testDebugMetadataCrossinline() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/debugMetadataCrossinline.kt");
}
@TestMetadata("delegatedProperties.kt")
public void testDelegatedProperties_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
@@ -3808,6 +3808,11 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("debugMetadataCrossinline.kt")
public void testDebugMetadataCrossinline() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/debugMetadataCrossinline.kt");
}
@TestMetadata("delegatedProperties.kt")
public void testDelegatedProperties_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
@@ -3808,6 +3808,11 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("debugMetadataCrossinline.kt")
public void testDebugMetadataCrossinline() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/debugMetadataCrossinline.kt");
}
@TestMetadata("delegatedProperties.kt")
public void testDelegatedProperties_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
@@ -3808,6 +3808,11 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("debugMetadataCrossinline.kt")
public void testDebugMetadataCrossinline() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/debugMetadataCrossinline.kt");
}
@TestMetadata("delegatedProperties.kt")
public void testDelegatedProperties_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");