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:
+5
-7
@@ -32,7 +32,8 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
|||||||
internal class FixStackAnalyzer(
|
internal class FixStackAnalyzer(
|
||||||
owner: String,
|
owner: String,
|
||||||
val method: MethodNode,
|
val method: MethodNode,
|
||||||
val context: FixStackContext
|
val context: FixStackContext,
|
||||||
|
private val skipBreakContinueGotoEdges: Boolean = true
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
// Stack size is always non-negative
|
// 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(
|
private inner class InternalAnalyzer(owner: String) : MethodAnalyzer<BasicValue>(owner, method, OptimizationBasicInterpreter()) {
|
||||||
owner: String,
|
|
||||||
method: MethodNode,
|
|
||||||
val context: FixStackContext
|
|
||||||
) : MethodAnalyzer<BasicValue>(owner, method, OptimizationBasicInterpreter()) {
|
|
||||||
val spilledStacks = hashMapOf<AbstractInsnNode, List<BasicValue>>()
|
val spilledStacks = hashMapOf<AbstractInsnNode, List<BasicValue>>()
|
||||||
var maxExtraStackSize = 0; private set
|
var maxExtraStackSize = 0; private set
|
||||||
|
|
||||||
override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean {
|
override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean {
|
||||||
|
if (!skipBreakContinueGotoEdges) return true
|
||||||
val insnNode = instructions[insn]
|
val insnNode = instructions[insn]
|
||||||
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
|
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ class FixStackMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun analyzeAndTransformSaveRestoreStack(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
|
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()
|
analyzer.analyze()
|
||||||
|
|
||||||
val actions = arrayListOf<() -> Unit>()
|
val actions = arrayListOf<() -> Unit>()
|
||||||
|
|||||||
Vendored
+18
@@ -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]
|
||||||
|
}
|
||||||
+12
@@ -4969,6 +4969,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("tryInsideCatch.kt")
|
||||||
public void testTryInsideCatch() throws Exception {
|
public void testTryInsideCatch() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
|
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);
|
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")
|
@TestMetadata("nullSpilling.kt")
|
||||||
public void testNullSpilling() throws Exception {
|
public void testNullSpilling() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
||||||
|
|||||||
@@ -4969,6 +4969,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("tryInsideCatch.kt")
|
||||||
public void testTryInsideCatch() throws Exception {
|
public void testTryInsideCatch() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
|
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);
|
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")
|
@TestMetadata("nullSpilling.kt")
|
||||||
public void testNullSpilling() throws Exception {
|
public void testNullSpilling() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
||||||
|
|||||||
@@ -4969,6 +4969,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("tryInsideCatch.kt")
|
||||||
public void testTryInsideCatch() throws Exception {
|
public void testTryInsideCatch() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
|
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);
|
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")
|
@TestMetadata("nullSpilling.kt")
|
||||||
public void testNullSpilling() throws Exception {
|
public void testNullSpilling() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
||||||
|
|||||||
+12
@@ -5671,6 +5671,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("tryInsideCatch.kt")
|
||||||
public void testTryInsideCatch() throws Exception {
|
public void testTryInsideCatch() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/tryInsideCatch.kt");
|
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);
|
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")
|
@TestMetadata("nullSpilling.kt")
|
||||||
public void testNullSpilling() throws Exception {
|
public void testNullSpilling() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user