KT-16713 Insufficient maximum stack size

1. Analyze method node with fake jumps for loops to make sure that
all instructions reachable only through break/continue jumps are processed.
2. Fix stack for break/continue jumps.
3. Drop fake jumps for loops, analyze method node again.
4. Fix stack for try/catch and beforeInline.
This commit is contained in:
Dmitry Petrov
2017-03-07 18:58:52 +03:00
committed by Mikhael Bogdanov
parent 80063b6f91
commit 11caa03427
10 changed files with 177 additions and 27 deletions
@@ -58,16 +58,9 @@ internal class FixStackAnalyzer(
for (marker in context.fakeAlwaysFalseIfeqMarkers) { for (marker in context.fakeAlwaysFalseIfeqMarkers) {
val next = marker.next val next = marker.next
if (next is JumpInsnNode) { if (next is JumpInsnNode) {
val nop = InsnNode(Opcodes.NOP) expectedStackNode[next.label] = marker
expectedStackNode[next.label] = nop
method.instructions.insert(next, nop)
method.instructions.remove(marker)
method.instructions.remove(next)
context.nodesToRemoveOnCleanup.add(nop)
} }
} }
context.fakeAlwaysFalseIfeqMarkers.clear()
} }
private val analyzer = InternalAnalyzer(owner, method, context) private val analyzer = InternalAnalyzer(owner, method, context)
@@ -39,8 +39,6 @@ internal class FixStackContext(val methodNode: MethodNode) {
val openingInlineMethodMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>() val openingInlineMethodMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
var consistentInlineMarkers: Boolean = true; private set var consistentInlineMarkers: Boolean = true; private set
val nodesToRemoveOnCleanup = arrayListOf<AbstractInsnNode>()
init { init {
saveStackMarkerForRestoreMarker = insertTryCatchBlocksMarkers(methodNode) saveStackMarkerForRestoreMarker = insertTryCatchBlocksMarkers(methodNode)
isThereAnyTryCatch = saveStackMarkerForRestoreMarker.isNotEmpty() isThereAnyTryCatch = saveStackMarkerForRestoreMarker.isNotEmpty()
@@ -39,31 +39,51 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
if (context.isAnalysisRequired()) { if (context.isAnalysisRequired()) {
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context) analyzeAndTransformBreakContinueGotos(context, internalClassName, methodNode)
analyzer.analyze() removeAlwaysFalseIfeqMarkers(context, methodNode)
analyzeAndTransformSaveRestoreStack(context, internalClassName, methodNode)
methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize
val actions = arrayListOf<() -> Unit>()
transformBreakContinueGotos(methodNode, context, actions, analyzer)
transformSaveRestoreStackMarkers(methodNode, context, actions, analyzer)
actions.forEach { it() }
} }
context.fakeAlwaysTrueIfeqMarkers.forEach { marker -> removeAlwaysTrueIfeqMarkers(context, methodNode)
replaceAlwaysTrueIfeqWithGoto(methodNode, marker) removeAlwaysFalseIfeqMarkers(context, methodNode)
} }
private fun analyzeAndTransformBreakContinueGotos(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
analyzer.analyze()
methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize
val actions = arrayListOf<() -> Unit>()
transformBreakContinueGotos(methodNode, context, actions, analyzer)
actions.forEach { it() }
}
private fun analyzeAndTransformSaveRestoreStack(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
analyzer.analyze()
val actions = arrayListOf<() -> Unit>()
transformSaveRestoreStackMarkers(methodNode, context, actions, analyzer)
actions.forEach { it() }
}
private fun removeAlwaysFalseIfeqMarkers(context: FixStackContext, methodNode: MethodNode) {
context.fakeAlwaysFalseIfeqMarkers.forEach { marker -> context.fakeAlwaysFalseIfeqMarkers.forEach { marker ->
removeAlwaysFalseIfeq(methodNode, marker) removeAlwaysFalseIfeq(methodNode, marker)
} }
context.fakeAlwaysFalseIfeqMarkers.clear()
}
context.nodesToRemoveOnCleanup.forEach { private fun removeAlwaysTrueIfeqMarkers(context: FixStackContext, methodNode: MethodNode) {
methodNode.instructions.remove(it) context.fakeAlwaysTrueIfeqMarkers.forEach { marker ->
replaceAlwaysTrueIfeqWithGoto(methodNode, marker)
} }
context.fakeAlwaysTrueIfeqMarkers.clear()
} }
private fun transformBreakContinueGotos( private fun transformBreakContinueGotos(
@@ -0,0 +1,30 @@
class MyQueue {
fun poll(): String? = null
}
class A {
val delayedQueue = MyQueue()
fun next() {
while (true) {
delayedQueue.poll() ?: break
}
while (true) {
unblock(delayedQueue.poll() ?: break)
}
while (true) {
unblock(delayedQueue.poll() ?: break)
}
}
fun unblock(p: String) {
}
}
fun box() : String {
A().next()
return "OK"
}
@@ -0,0 +1,32 @@
class MyQueue {
fun poll(): String? = null
}
class A {
val delayedQueue = MyQueue()
var cond = true
fun next() {
while (cond) {
delayedQueue.poll() ?: break
}
while (cond) {
unblock(delayedQueue.poll() ?: break)
}
while (cond) {
unblock(delayedQueue.poll() ?: break)
}
}
fun unblock(p: String) {
}
}
fun box() : String {
A().next()
return "OK"
}
@@ -0,0 +1,19 @@
@kotlin.Metadata
public final class A {
private final @org.jetbrains.annotations.NotNull field delayedQueue: MyQueue
public method <init>(): void
public final @org.jetbrains.annotations.NotNull method getDelayedQueue(): MyQueue
public final method next(): void
public final method unblock(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
}
@kotlin.Metadata
public final class Kt16713Kt {
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
@kotlin.Metadata
public final class MyQueue {
public method <init>(): void
public final @org.jetbrains.annotations.Nullable method poll(): java.lang.String
}
@@ -0,0 +1,22 @@
@kotlin.Metadata
public final class A {
private field cond: boolean
private final @org.jetbrains.annotations.NotNull field delayedQueue: MyQueue
public method <init>(): void
public final method getCond(): boolean
public final @org.jetbrains.annotations.NotNull method getDelayedQueue(): MyQueue
public final method next(): void
public final method setCond(p0: boolean): void
public final method unblock(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
}
@kotlin.Metadata
public final class Kt16713_2Kt {
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
@kotlin.Metadata
public final class MyQueue {
public method <init>(): void
public final @org.jetbrains.annotations.Nullable method poll(): java.lang.String
}
@@ -4456,6 +4456,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt16713.kt")
public void testKt16713() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt16713.kt");
doTest(fileName);
}
@TestMetadata("kt16713_2.kt")
public void testKt16713_2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt16713_2.kt");
doTest(fileName);
}
@TestMetadata("kt9022And.kt") @TestMetadata("kt9022And.kt")
public void testKt9022And() throws Exception { public void testKt9022And() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt9022And.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt9022And.kt");
@@ -4456,6 +4456,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt16713.kt")
public void testKt16713() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt16713.kt");
doTest(fileName);
}
@TestMetadata("kt16713_2.kt")
public void testKt16713_2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt16713_2.kt");
doTest(fileName);
}
@TestMetadata("kt9022And.kt") @TestMetadata("kt9022And.kt")
public void testKt9022And() throws Exception { public void testKt9022And() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt9022And.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt9022And.kt");
@@ -5153,6 +5153,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt16713.kt")
public void testKt16713() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt16713.kt");
doTest(fileName);
}
@TestMetadata("kt16713_2.kt")
public void testKt16713_2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt16713_2.kt");
doTest(fileName);
}
@TestMetadata("kt9022And.kt") @TestMetadata("kt9022And.kt")
public void testKt9022And() throws Exception { public void testKt9022And() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt9022And.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakContinueInExpressions/kt9022And.kt");