Add $completion entry to suspend functions' LVT

#KT-28810 Fixed
This commit is contained in:
Ilmir Usmanov
2019-02-20 17:55:11 +03:00
parent 677987c85f
commit ec7315e6da
11 changed files with 200 additions and 9 deletions
@@ -49,6 +49,8 @@ private const val COROUTINES_METADATA_METHOD_NAME_JVM_NAME = "m"
private const val COROUTINES_METADATA_CLASS_NAME_JVM_NAME = "c"
private const val COROUTINES_METADATA_VERSION_JVM_NAME = "v"
const val SUSPEND_FUNCTION_CONTINUATION_PARAMETER = "\$completion"
class CoroutineTransformerMethodVisitor(
delegate: MethodVisitor,
access: Int,
@@ -105,6 +107,8 @@ class CoroutineTransformerMethodVisitor(
if (isForNamedFunction) {
ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode)
addCompletionParameterToLVT(methodNode)
if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) {
dropSuspensionMarkers(methodNode, suspensionPoints)
return
@@ -197,6 +201,32 @@ class CoroutineTransformerMethodVisitor(
}
}
private fun addCompletionParameterToLVT(methodNode: MethodNode) {
val index =
/* all args */ Type.getMethodType(methodNode.desc).argumentTypes.fold(0) { a, b -> a + b.size } +
/* this */ (if (isStatic(methodNode.access)) 0 else 1) -
/* only last */ 1
val startLabel = with(methodNode.instructions) {
if (first is LabelNode) first as LabelNode
else LabelNode().also { insertBefore(first, it) }
}
val endLabel = with(methodNode.instructions) {
if (last is LabelNode) last as LabelNode
else LabelNode().also { insert(last, it) }
}
methodNode.localVariables.add(
LocalVariableNode(
SUSPEND_FUNCTION_CONTINUATION_PARAMETER,
languageVersionSettings.continuationAsmType().descriptor,
null,
startLabel,
endLabel,
index
)
)
}
private fun findSuspensionPointLineNumber(suspensionPoint: SuspensionPoint) =
suspensionPoint.suspensionCallBegin.findPreviousOrNull { it is LineNumberNode } as LineNumberNode?
@@ -238,15 +268,11 @@ class CoroutineTransformerMethodVisitor(
}
private fun fixLvtForParameters(methodNode: MethodNode, startLabel: LabelNode, endLabel: LabelNode) {
// We need to skip continuation, since the inliner likes to remap variables there.
// But this is not a problem, since we have separate $continuation LVT entry
val paramsNum =
/* this */ (if (internalNameForDispatchReceiver != null) 1 else 0) +
/* real params */ Type.getArgumentTypes(methodNode.desc).size -
/* no continuation */ if (isForNamedFunction) 1 else 0
/* this */ (if (isStatic(methodNode.access)) 0 else 1) +
/* real params */ Type.getArgumentTypes(methodNode.desc).fold(0) { a, b -> a + b.size }
for (i in 0..paramsNum) {
for (i in 0 until paramsNum) {
fixRangeOfLvtRecord(methodNode, i, startLabel, endLabel)
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_CONTINUATION_PARAMETER
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.org.objectweb.asm.Type
@@ -80,7 +81,7 @@ private fun useVar(
val index = node.instructions.indexOf(insn)
node.localVariables.filter {
// Inliner fake variables, despite being present in LVT, are not read, thus are always dead
!it.name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) && !it.name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) &&
!it.name.isInvisibleDebuggerVariable() &&
node.instructions.indexOf(it.start) < index && index < node.instructions.indexOf(it.end) &&
Type.getType(it.desc).sort == typeAnnotatedFrame?.getLocal(it.index)?.type?.sort
}.forEach {
@@ -93,3 +94,8 @@ private fun useVar(
frame.markAlive(insn.`var`)
}
}
private fun String.isInvisibleDebuggerVariable(): Boolean =
startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) ||
startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) ||
this == SUSPEND_FUNCTION_CONTINUATION_PARAMETER