Add $completion entry to suspend functions' LVT
#KT-28810 Fixed
This commit is contained in:
+33
-7
@@ -49,6 +49,8 @@ private const val COROUTINES_METADATA_METHOD_NAME_JVM_NAME = "m"
|
||||
private const val COROUTINES_METADATA_CLASS_NAME_JVM_NAME = "c"
|
||||
private const val COROUTINES_METADATA_VERSION_JVM_NAME = "v"
|
||||
|
||||
const val SUSPEND_FUNCTION_CONTINUATION_PARAMETER = "\$completion"
|
||||
|
||||
class CoroutineTransformerMethodVisitor(
|
||||
delegate: MethodVisitor,
|
||||
access: Int,
|
||||
@@ -105,6 +107,8 @@ class CoroutineTransformerMethodVisitor(
|
||||
if (isForNamedFunction) {
|
||||
ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||
|
||||
addCompletionParameterToLVT(methodNode)
|
||||
|
||||
if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) {
|
||||
dropSuspensionMarkers(methodNode, suspensionPoints)
|
||||
return
|
||||
@@ -197,6 +201,32 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCompletionParameterToLVT(methodNode: MethodNode) {
|
||||
val index =
|
||||
/* all args */ Type.getMethodType(methodNode.desc).argumentTypes.fold(0) { a, b -> a + b.size } +
|
||||
/* this */ (if (isStatic(methodNode.access)) 0 else 1) -
|
||||
/* only last */ 1
|
||||
val startLabel = with(methodNode.instructions) {
|
||||
if (first is LabelNode) first as LabelNode
|
||||
else LabelNode().also { insertBefore(first, it) }
|
||||
}
|
||||
|
||||
val endLabel = with(methodNode.instructions) {
|
||||
if (last is LabelNode) last as LabelNode
|
||||
else LabelNode().also { insert(last, it) }
|
||||
}
|
||||
methodNode.localVariables.add(
|
||||
LocalVariableNode(
|
||||
SUSPEND_FUNCTION_CONTINUATION_PARAMETER,
|
||||
languageVersionSettings.continuationAsmType().descriptor,
|
||||
null,
|
||||
startLabel,
|
||||
endLabel,
|
||||
index
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun findSuspensionPointLineNumber(suspensionPoint: SuspensionPoint) =
|
||||
suspensionPoint.suspensionCallBegin.findPreviousOrNull { it is LineNumberNode } as LineNumberNode?
|
||||
|
||||
@@ -238,15 +268,11 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
|
||||
private fun fixLvtForParameters(methodNode: MethodNode, startLabel: LabelNode, endLabel: LabelNode) {
|
||||
// We need to skip continuation, since the inliner likes to remap variables there.
|
||||
// But this is not a problem, since we have separate $continuation LVT entry
|
||||
|
||||
val paramsNum =
|
||||
/* this */ (if (internalNameForDispatchReceiver != null) 1 else 0) +
|
||||
/* real params */ Type.getArgumentTypes(methodNode.desc).size -
|
||||
/* no continuation */ if (isForNamedFunction) 1 else 0
|
||||
/* this */ (if (isStatic(methodNode.access)) 0 else 1) +
|
||||
/* real params */ Type.getArgumentTypes(methodNode.desc).fold(0) { a, b -> a + b.size }
|
||||
|
||||
for (i in 0..paramsNum) {
|
||||
for (i in 0 until paramsNum) {
|
||||
fixRangeOfLvtRecord(methodNode, i, startLabel, endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_CONTINUATION_PARAMETER
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -80,7 +81,7 @@ private fun useVar(
|
||||
val index = node.instructions.indexOf(insn)
|
||||
node.localVariables.filter {
|
||||
// Inliner fake variables, despite being present in LVT, are not read, thus are always dead
|
||||
!it.name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) && !it.name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) &&
|
||||
!it.name.isInvisibleDebuggerVariable() &&
|
||||
node.instructions.indexOf(it.start) < index && index < node.instructions.indexOf(it.end) &&
|
||||
Type.getType(it.desc).sort == typeAnnotatedFrame?.getLocal(it.index)?.type?.sort
|
||||
}.forEach {
|
||||
@@ -93,3 +94,8 @@ private fun useVar(
|
||||
frame.markAlive(insn.`var`)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.isInvisibleDebuggerVariable(): Boolean =
|
||||
startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) ||
|
||||
startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) ||
|
||||
this == SUSPEND_FUNCTION_CONTINUATION_PARAMETER
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
class A {
|
||||
suspend fun foo() {}
|
||||
}
|
||||
|
||||
// METHOD : A.foo(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
// VARIABLE : NAME=this TYPE=LA; INDEX=0
|
||||
// VARIABLE : NAME=$completion TYPE=Lkotlin/coroutines/Continuation; INDEX=1
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
class A {
|
||||
suspend fun foo() {}
|
||||
suspend fun foo1(l: Long) {
|
||||
foo()
|
||||
foo()
|
||||
}
|
||||
}
|
||||
|
||||
// METHOD : A.foo1(JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
// VARIABLE : NAME=this TYPE=LA; INDEX=0
|
||||
// VARIABLE : NAME=l TYPE=J INDEX=1
|
||||
// VARIABLE : NAME=$completion TYPE=Lkotlin/coroutines/Continuation; INDEX=3
|
||||
// VARIABLE : NAME=$continuation TYPE=Lkotlin/coroutines/Continuation; INDEX=5
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
suspend fun foo() {}
|
||||
|
||||
// METHOD : StaticSimpleKt.foo(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
// VARIABLE : NAME=$completion TYPE=Lkotlin/coroutines/Continuation; INDEX=0
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
class A
|
||||
|
||||
suspend fun A.foo() {}
|
||||
|
||||
// METHOD : StaticSimpleReceiverKt.foo(LA;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
// VARIABLE : NAME=$receiver TYPE=LA; INDEX=0
|
||||
// VARIABLE : NAME=$completion TYPE=Lkotlin/coroutines/Continuation; INDEX=1
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
suspend fun foo() {}
|
||||
suspend fun foo1(l: Long) {
|
||||
foo()
|
||||
foo()
|
||||
}
|
||||
|
||||
// METHOD : StaticStateMachineKt.foo1(JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
// VARIABLE : NAME=l TYPE=J INDEX=0
|
||||
// VARIABLE : NAME=$completion TYPE=Lkotlin/coroutines/Continuation; INDEX=2
|
||||
// VARIABLE : NAME=$continuation TYPE=Lkotlin/coroutines/Continuation; INDEX=4
|
||||
compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticStateMachineReceiver.kt
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
class A
|
||||
|
||||
suspend fun A.foo() {}
|
||||
suspend fun A.foo1(l: Long) {
|
||||
foo()
|
||||
foo()
|
||||
}
|
||||
|
||||
// METHOD : StaticStateMachineReceiverKt.foo1(LA;JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
// VARIABLE : NAME=$receiver TYPE=LA; INDEX=0
|
||||
// VARIABLE : NAME=l TYPE=J INDEX=1
|
||||
// VARIABLE : NAME=$completion TYPE=Lkotlin/coroutines/Continuation; INDEX=3
|
||||
// VARIABLE : NAME=$continuation TYPE=Lkotlin/coroutines/Continuation; INDEX=5
|
||||
+2
-1
@@ -14,4 +14,5 @@ suspend fun test() = foo(A("O", "K")) { (x_param, y_param) -> x_param + y_param
|
||||
// VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=8
|
||||
// VARIABLE : NAME=$i$a$-foo-InlineKt$test$2 TYPE=I INDEX=5
|
||||
// VARIABLE : NAME=a$iv TYPE=LA; INDEX=1
|
||||
// VARIABLE : NAME=$i$f$foo TYPE=I INDEX=2
|
||||
// VARIABLE : NAME=$i$f$foo TYPE=I INDEX=2
|
||||
// VARIABLE : NAME=$completion TYPE=Lkotlin/coroutines/Continuation; INDEX=0
|
||||
+43
@@ -109,6 +109,49 @@ public class CheckLocalVariablesTableTestGenerated extends AbstractCheckLocalVar
|
||||
runTest("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CompletionInSuspendFunction extends AbstractCheckLocalVariablesTableTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCompletionInSuspendFunction() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("nonStaticSimple.kt")
|
||||
public void testNonStaticSimple() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/nonStaticSimple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonStaticStateMachine.kt")
|
||||
public void testNonStaticStateMachine() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/nonStaticStateMachine.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticSimple.kt")
|
||||
public void testStaticSimple() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticSimple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticSimpleReceiver.kt")
|
||||
public void testStaticSimpleReceiver() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticSimpleReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticStateMachine.kt")
|
||||
public void testStaticStateMachine() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticStateMachine.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticStateMachineReceiver.kt")
|
||||
public void testStaticStateMachineReceiver() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticStateMachineReceiver.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+43
@@ -109,6 +109,49 @@ public class IrCheckLocalVariablesTableTestGenerated extends AbstractIrCheckLoca
|
||||
runTest("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CompletionInSuspendFunction extends AbstractIrCheckLocalVariablesTableTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCompletionInSuspendFunction() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("nonStaticSimple.kt")
|
||||
public void testNonStaticSimple() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/nonStaticSimple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonStaticStateMachine.kt")
|
||||
public void testNonStaticStateMachine() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/nonStaticStateMachine.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticSimple.kt")
|
||||
public void testStaticSimple() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticSimple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticSimpleReceiver.kt")
|
||||
public void testStaticSimpleReceiver() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticSimpleReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticStateMachine.kt")
|
||||
public void testStaticStateMachine() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticStateMachine.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticStateMachineReceiver.kt")
|
||||
public void testStaticStateMachineReceiver() throws Exception {
|
||||
runTest("compiler/testData/checkLocalVariablesTable/completionInSuspendFunction/staticStateMachineReceiver.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user