Fix exception after combination of while (true) + stack-spilling

FixStack transformation divides on phases:
- Fixing stack before break/continue
- Fixing stack for inline markers/try-catch blocks

After the first stage all ALWAYS_TRUE markers are replaced
with simple GOTO's and if we're skipping break/continue edges
we won't reach the code after while (true) statement.

At the same time it's fine to not to skip them in the second phase
as the stack for them is already corrected in the first phase

 #KT-19475 Fixed
This commit is contained in:
Denis Zharkov
2017-08-08 12:50:14 +07:00
parent 45d5f6a950
commit ff0736f09e
8 changed files with 104 additions and 8 deletions
@@ -32,7 +32,8 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
internal class FixStackAnalyzer(
owner: String,
val method: MethodNode,
val context: FixStackContext
val context: FixStackContext,
private val skipBreakContinueGotoEdges: Boolean = true
) {
companion object {
// Stack size is always non-negative
@@ -65,17 +66,14 @@ internal class FixStackAnalyzer(
}
}
private val analyzer = InternalAnalyzer(owner, method, context)
private val analyzer = InternalAnalyzer(owner)
private class InternalAnalyzer(
owner: String,
method: MethodNode,
val context: FixStackContext
) : MethodAnalyzer<BasicValue>(owner, method, OptimizationBasicInterpreter()) {
private inner class InternalAnalyzer(owner: String) : MethodAnalyzer<BasicValue>(owner, method, OptimizationBasicInterpreter()) {
val spilledStacks = hashMapOf<AbstractInsnNode, List<BasicValue>>()
var maxExtraStackSize = 0; private set
override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean {
if (!skipBreakContinueGotoEdges) return true
val insnNode = instructions[insn]
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
}
@@ -64,7 +64,7 @@ class FixStackMethodTransformer : MethodTransformer() {
}
private fun analyzeAndTransformSaveRestoreStack(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context, skipBreakContinueGotoEdges = false)
analyzer.analyze()
val actions = arrayListOf<() -> Unit>()
@@ -0,0 +1,18 @@
// WITH_RUNTIME
fun box(): String {
val a = arrayListOf<String>()
while (true) {
if (a.size == 0) {
break
}
}
for (i in 1..2) {
a.add(try { foo() } catch (e: Throwable) { "OK" })
}
return a[0]
}
fun foo(): String = throw RuntimeException()
@@ -0,0 +1,32 @@
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
suspend fun suspendHere(): String = suspendCoroutineOrReturn { x ->
x.resume("OK")
COROUTINE_SUSPENDED
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun box(): String {
var result = arrayListOf<String>()
builder {
while (true) {
if (result.size == 0) {
break
}
}
for (i in 1..2) {
result.add(suspendHere())
}
}
return result[0]
}
@@ -4969,6 +4969,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("tryCatchAfterWhileTrue.kt")
public void testTryCatchAfterWhileTrue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryCatchAfterWhileTrue.kt");
doTest(fileName);
}
@TestMetadata("tryInsideCatch.kt")
public void testTryInsideCatch() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
@@ -5944,6 +5950,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/varSpilling"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("kt19475.kt")
public void testKt19475() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt");
doTest(fileName);
}
@TestMetadata("nullSpilling.kt")
public void testNullSpilling() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
@@ -4969,6 +4969,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("tryCatchAfterWhileTrue.kt")
public void testTryCatchAfterWhileTrue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryCatchAfterWhileTrue.kt");
doTest(fileName);
}
@TestMetadata("tryInsideCatch.kt")
public void testTryInsideCatch() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
@@ -5944,6 +5950,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/varSpilling"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("kt19475.kt")
public void testKt19475() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt");
doTest(fileName);
}
@TestMetadata("nullSpilling.kt")
public void testNullSpilling() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
@@ -4969,6 +4969,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("tryCatchAfterWhileTrue.kt")
public void testTryCatchAfterWhileTrue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryCatchAfterWhileTrue.kt");
doTest(fileName);
}
@TestMetadata("tryInsideCatch.kt")
public void testTryInsideCatch() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
@@ -5944,6 +5950,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/varSpilling"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("kt19475.kt")
public void testKt19475() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt");
doTest(fileName);
}
@TestMetadata("nullSpilling.kt")
public void testNullSpilling() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
@@ -5671,6 +5671,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("tryCatchAfterWhileTrue.kt")
public void testTryCatchAfterWhileTrue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryCatchAfterWhileTrue.kt");
doTest(fileName);
}
@TestMetadata("tryInsideCatch.kt")
public void testTryInsideCatch() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
@@ -6628,6 +6634,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/varSpilling"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("kt19475.kt")
public void testKt19475() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt");
doTest(fileName);
}
@TestMetadata("nullSpilling.kt")
public void testNullSpilling() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");