From e75b6c8404d5d8679e89e1e468f41e3c53103df2 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 12 May 2017 15:50:10 +0300 Subject: [PATCH] Perform fix-stack transformation before method analysis Otherwise it might fail on an "invalid" bytecode --- .../CoroutineTransformerMethodVisitor.kt | 22 ++++++++-- .../breakWithNonEmptyStack.kt | 40 +++++++++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 +++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 +++ .../LightAnalysisModeTestGenerated.java | 6 +++ .../semantics/JsCodegenBoxTestGenerated.java | 6 +++ 6 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index bc782060c3d..86f89424d83 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -22,9 +22,10 @@ import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.TransformationMethodVisitor import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil +import org.jetbrains.kotlin.codegen.inline.MaxStackFrameSizeAndLocalsCalculator import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer -import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer import org.jetbrains.kotlin.codegen.optimization.common.* +import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer import org.jetbrains.kotlin.codegen.optimization.fixStack.top import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.resolve.jvm.AsmTypes @@ -67,6 +68,8 @@ class CoroutineTransformerMethodVisitor( // First instruction in the method node may change in case of named function val actualCoroutineStart = methodNode.instructions.first + FixStackMethodTransformer().transform(containingClassInternalName, methodNode) + if (isForNamedFunction) { if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) { dropSuspensionMarkers(methodNode, suspensionPoints) @@ -83,8 +86,8 @@ class CoroutineTransformerMethodVisitor( splitTryCatchBlocksContainingSuspensionPoint(methodNode, suspensionPoint) } - // Spill stack to variables before suspension points, try/catch blocks - FixStackWithLabelNormalizationMethodTransformer().transform(containingClassInternalName, methodNode) + // Actual max stack might be increased during the previous phases + updateMaxStack(methodNode) // Remove unreachable suspension points // If we don't do this, then relevant frames will not be analyzed, that is unexpected from point of view of next steps (e.g. variable spilling) @@ -135,7 +138,20 @@ class CoroutineTransformerMethodVisitor( dropSuspensionMarkers(methodNode, suspensionPoints) methodNode.removeEmptyCatchBlocks() + } + private fun updateMaxStack(methodNode: MethodNode) { + methodNode.instructions.resetLabels() + methodNode.accept( + MaxStackFrameSizeAndLocalsCalculator( + Opcodes.ASM5, methodNode.access, methodNode.desc, + object : MethodVisitor(Opcodes.ASM5) { + override fun visitMaxs(maxStack: Int, maxLocals: Int) { + methodNode.maxStack = maxStack + } + } + ) + ) } private fun prepareMethodNodePreludeForNamedFunction(methodNode: MethodNode) { diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt new file mode 100644 index 00000000000..52453e44edd --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt @@ -0,0 +1,40 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND: NATIVE +import helpers.* +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +class A { + var result = mutableListOf("O", "K", null) + suspend fun foo(): String? = suspendCoroutineOrReturn { x -> + x.resume(result.removeAt(0)) + COROUTINE_SUSPENDED + } +} + +var result = "" + +suspend fun append(ignore: String, x: String) { + result += x +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +suspend fun bar() { + val a = A() + while (true) { + append("ignore", a.foo() ?: break) + } +} + +fun box(): String { + builder { + bar() + } + + return result +} + diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 68af09b298b..43fb40c917b 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -5407,6 +5407,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("breakWithNonEmptyStack.kt") + public void testBreakWithNonEmptyStack() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt"); + doTest(fileName); + } + @TestMetadata("destructuringInLambdas.kt") public void testDestructuringInLambdas() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 154b9e85131..4aea6082a3e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5407,6 +5407,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("breakWithNonEmptyStack.kt") + public void testBreakWithNonEmptyStack() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt"); + doTest(fileName); + } + @TestMetadata("destructuringInLambdas.kt") public void testDestructuringInLambdas() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 28a8c86a5e9..b8ee6391973 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -5407,6 +5407,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("breakWithNonEmptyStack.kt") + public void testBreakWithNonEmptyStack() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt"); + doTest(fileName); + } + @TestMetadata("destructuringInLambdas.kt") public void testDestructuringInLambdas() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 892d3caa5e6..81eb0b23f03 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -6116,6 +6116,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("breakWithNonEmptyStack.kt") + public void testBreakWithNonEmptyStack() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/breakWithNonEmptyStack.kt"); + doTest(fileName); + } + @TestMetadata("destructuringInLambdas.kt") public void testDestructuringInLambdas() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt");