Do not restore stack in default handler for try-finally

It never terminates, so the corresponding value on stack can't be used.
However, if this happens in an inlined lambda argument, the inliner is
unable to remove the corresponding ALOAD instruction (because default
handler never terminates, and thus corresponding ALOAD is not used for
lambda invocation).

KT-17573 try-finally expression in inlined function parameter argument fails with VerifyError
This commit is contained in:
Dmitry Petrov
2017-04-25 12:25:32 +03:00
parent e91f4cf65a
commit e1731373d8
8 changed files with 83 additions and 4 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.optimization.fixStack
import org.jetbrains.kotlin.codegen.optimization.common.findNextOrNull import org.jetbrains.kotlin.codegen.optimization.common.findNextOrNull
import org.jetbrains.kotlin.codegen.optimization.common.hasOpcode import org.jetbrains.kotlin.codegen.optimization.common.hasOpcode
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
@@ -117,6 +118,11 @@ private fun insertSaveRestoreStackMarkers(
private fun collectDecompiledTryDescriptors(methodNode: MethodNode): Map<LabelNode, DecompiledTryDescriptor> { private fun collectDecompiledTryDescriptors(methodNode: MethodNode): Map<LabelNode, DecompiledTryDescriptor> {
val decompiledTryDescriptorForStart: MutableMap<LabelNode, DecompiledTryDescriptor> = hashMapOf() val decompiledTryDescriptorForStart: MutableMap<LabelNode, DecompiledTryDescriptor> = hashMapOf()
val decompiledTryDescriptorForHandler: MutableMap<LabelNode, DecompiledTryDescriptor> = hashMapOf() val decompiledTryDescriptorForHandler: MutableMap<LabelNode, DecompiledTryDescriptor> = hashMapOf()
val defaultHandlers = methodNode.tryCatchBlocks.mapNotNullTo(SmartSet.create()) {
if (it.isDefaultHandlerNode()) it.handler else null
}
for (tcb in methodNode.tryCatchBlocks) { for (tcb in methodNode.tryCatchBlocks) {
if (tcb.isDefaultHandlerNode()) { if (tcb.isDefaultHandlerNode()) {
assert(decompiledTryDescriptorForHandler.containsKey(tcb.start)) { "${methodNode.debugString(tcb)}: default handler should occur after some regular handler" } assert(decompiledTryDescriptorForHandler.containsKey(tcb.start)) { "${methodNode.debugString(tcb)}: default handler should occur after some regular handler" }
@@ -127,9 +133,8 @@ private fun collectDecompiledTryDescriptors(methodNode: MethodNode): Map<LabelNo
DecompiledTryDescriptor(tcb.start) DecompiledTryDescriptor(tcb.start)
} }
} }
with(decompiledTryDescriptor) {
handlerStartLabels.add(tcb.handler)
with(decompiledTryDescriptor) {
if (tcb.isDefaultHandlerNode()) { if (tcb.isDefaultHandlerNode()) {
assert(defaultHandlerTcb == null) { assert(defaultHandlerTcb == null) {
"${methodNode.debugString(tcb)}: default handler is already found: ${methodNode.debugString(defaultHandlerTcb!!)}" "${methodNode.debugString(tcb)}: default handler is already found: ${methodNode.debugString(defaultHandlerTcb!!)}"
@@ -137,6 +142,10 @@ private fun collectDecompiledTryDescriptors(methodNode: MethodNode): Map<LabelNo
defaultHandlerTcb = tcb defaultHandlerTcb = tcb
} }
if (tcb.handler !in defaultHandlers) {
handlerStartLabels.add(tcb.handler)
}
} }
} }
@@ -33,14 +33,13 @@ internal class FixStackContext(val methodNode: MethodNode) {
val fakeAlwaysFalseIfeqMarkers = arrayListOf<AbstractInsnNode>() val fakeAlwaysFalseIfeqMarkers = arrayListOf<AbstractInsnNode>()
val isThereAnyTryCatch: Boolean val isThereAnyTryCatch: Boolean
val saveStackMarkerForRestoreMarker: Map<AbstractInsnNode, AbstractInsnNode> val saveStackMarkerForRestoreMarker = insertTryCatchBlocksMarkers(methodNode)
val restoreStackMarkersForSaveMarker = hashMapOf<AbstractInsnNode, MutableList<AbstractInsnNode>>() val restoreStackMarkersForSaveMarker = hashMapOf<AbstractInsnNode, MutableList<AbstractInsnNode>>()
val openingInlineMethodMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>() val openingInlineMethodMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
var consistentInlineMarkers: Boolean = true; private set var consistentInlineMarkers: Boolean = true; private set
init { init {
saveStackMarkerForRestoreMarker = insertTryCatchBlocksMarkers(methodNode)
isThereAnyTryCatch = saveStackMarkerForRestoreMarker.isNotEmpty() isThereAnyTryCatch = saveStackMarkerForRestoreMarker.isNotEmpty()
for ((restore, save) in saveStackMarkerForRestoreMarker) { for ((restore, save) in saveStackMarkerForRestoreMarker) {
restoreStackMarkersForSaveMarker.getOrPut(save) { SmartList() }.add(restore) restoreStackMarkersForSaveMarker.getOrPut(save) { SmartList() }.add(restore)
@@ -0,0 +1,6 @@
fun zap(s: String) = s
inline fun tryZap(string: String, fn: (String) -> String) =
fn(try { zap(string) } finally {})
fun box(): String = tryZap("OK") { it }
@@ -0,0 +1,17 @@
fun zap(s: String) = s
inline fun tryZap1(string: String, fn: (String) -> String) =
fn(
try { zap(string) } finally {
try { zap(string) } finally { }
}
)
inline fun tryZap2(string: String, fn: (String) -> String) =
fn(
try { zap(string) } finally {
try { zap(string) } catch (e: Exception) { }
}
)
fun box(): String = tryZap1("O") { it } + tryZap2("K") { it }
@@ -4810,6 +4810,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt17573.kt")
public void testKt17573() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573.kt");
doTest(fileName);
}
@TestMetadata("kt17573_nested.kt")
public void testKt17573_nested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573_nested.kt");
doTest(fileName);
}
@TestMetadata("kt8608.kt") @TestMetadata("kt8608.kt")
public void testKt8608() throws Exception { public void testKt8608() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt");
@@ -4810,6 +4810,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt17573.kt")
public void testKt17573() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573.kt");
doTest(fileName);
}
@TestMetadata("kt17573_nested.kt")
public void testKt17573_nested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573_nested.kt");
doTest(fileName);
}
@TestMetadata("kt8608.kt") @TestMetadata("kt8608.kt")
public void testKt8608() throws Exception { public void testKt8608() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt");
@@ -4810,6 +4810,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt17573.kt")
public void testKt17573() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573.kt");
doTest(fileName);
}
@TestMetadata("kt17573_nested.kt")
public void testKt17573_nested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573_nested.kt");
doTest(fileName);
}
@TestMetadata("kt8608.kt") @TestMetadata("kt8608.kt")
public void testKt8608() throws Exception { public void testKt8608() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt");
@@ -5525,6 +5525,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt17573.kt")
public void testKt17573() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573.kt");
doTest(fileName);
}
@TestMetadata("kt17573_nested.kt")
public void testKt17573_nested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt17573_nested.kt");
doTest(fileName);
}
@TestMetadata("kt8608.kt") @TestMetadata("kt8608.kt")
public void testKt8608() throws Exception { public void testKt8608() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions/kt8608.kt");