Ignore unreachable code on tail call optimization

#KT-21759: Fixed
This commit is contained in:
Ilmir Usmanov
2018-01-12 19:04:42 +03:00
parent 25998c1f9b
commit ed11528664
10 changed files with 299 additions and 3 deletions
@@ -41,6 +41,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
@@ -689,14 +690,16 @@ private fun allSuspensionPointsAreTailCalls(
methodNode: MethodNode,
suspensionPoints: List<SuspensionPoint>
): Boolean {
val safelyReachableReturns = findSafelyReachableReturns(methodNode)
val sourceFrames = MethodTransformer.analyze(thisName, methodNode, IgnoringCopyOperationSourceInterpreter())
val safelyReachableReturns = findSafelyReachableReturns(methodNode, sourceFrames)
val instructions = methodNode.instructions
return suspensionPoints.all { suspensionPoint ->
val beginIndex = instructions.indexOf(suspensionPoint.suspensionCallBegin)
val endIndex = instructions.indexOf(suspensionPoint.suspensionCallEnd)
if (isUnreachable(beginIndex, sourceFrames)) return@all true
val insideTryBlock = methodNode.tryCatchBlocks.any { block ->
val tryBlockStartIndex = instructions.indexOf(block.start)
val tryBlockEndIndex = instructions.indexOf(block.end)
@@ -728,7 +731,7 @@ internal class IgnoringCopyOperationSourceInterpreter : SourceInterpreter() {
*
* @return indices of safely reachable returns for each instruction in the method node
*/
private fun findSafelyReachableReturns(methodNode: MethodNode): Array<Set<Int>?> {
private fun findSafelyReachableReturns(methodNode: MethodNode, sourceFrames: Array<Frame<SourceValue?>?>): Array<Set<Int>?> {
val controlFlowGraph = ControlFlowGraph.build(methodNode)
val insns = methodNode.instructions
@@ -736,6 +739,7 @@ private fun findSafelyReachableReturns(methodNode: MethodNode): Array<Set<Int>?>
val insn = insns[index]
if (insn.opcode == Opcodes.ARETURN) {
if (isUnreachable(index, sourceFrames)) return@init null
return@init setOf(index)
}
@@ -770,6 +774,9 @@ private fun findSafelyReachableReturns(methodNode: MethodNode): Array<Set<Int>?>
return reachableReturnsIndices
}
// Check whether this instruction is unreachable, i.e. there is no path leading to this instruction
internal fun isUnreachable(index: Int, sourceFrames: Array<Frame<SourceValue?>?>) = sourceFrames[index] == null
private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): Boolean {
val insns = methodNode.instructions
val index = insns.indexOf(this)
@@ -112,7 +112,9 @@ object ReturnUnitMethodTransformer : MethodTransformer() {
): Map<AbstractInsnNode, Collection<AbstractInsnNode>> {
val frames = analyze(internalClassName, methodNode, IgnoringCopyOperationSourceInterpreter())
return pops.keysToMap {
frames[methodNode.instructions.indexOf(it)].getStack(0).insns
val index = methodNode.instructions.indexOf(it)
if (isUnreachable(index, frames)) return@keysToMap emptySet<AbstractInsnNode>()
frames[index].getStack(0).insns
}
}