Initialize fake inliner variables on resume path

Otherwise, this will cause R8 to complain.
This commit is contained in:
Ilmir Usmanov
2020-08-04 21:38:30 +02:00
parent bb5a99ec18
commit 68342a1f72
2 changed files with 33 additions and 9 deletions
@@ -141,8 +141,9 @@ class CoroutineTransformerMethodVisitor(
val suspensionPointLineNumbers = suspensionPoints.map { findSuspensionPointLineNumber(it) } val suspensionPointLineNumbers = suspensionPoints.map { findSuspensionPointLineNumber(it) }
val continuationLabels = suspensionPoints.withIndex().map { // Create states in state-machine, to which state-machine can jump
transformCallAndReturnContinuationLabel( val stateLabels = suspensionPoints.withIndex().map {
transformCallAndReturnStateLabel(
it.index + 1, it.value, methodNode, suspendMarkerVarIndex, suspensionPointLineNumbers[it.index] it.index + 1, it.value, methodNode, suspendMarkerVarIndex, suspensionPointLineNumbers[it.index]
) )
} }
@@ -167,7 +168,7 @@ class CoroutineTransformerMethodVisitor(
0, 0,
suspensionPoints.size, suspensionPoints.size,
defaultLabel, defaultLabel,
firstStateLabel, *continuationLabels.toTypedArray() firstStateLabel, *stateLabels.toTypedArray()
), ),
firstStateLabel firstStateLabel
) )
@@ -184,6 +185,8 @@ class CoroutineTransformerMethodVisitor(
}) })
} }
initializeFakeInlinerVariables(methodNode, stateLabels)
dropSuspensionMarkers(methodNode) dropSuspensionMarkers(methodNode)
methodNode.removeEmptyCatchBlocks() methodNode.removeEmptyCatchBlocks()
@@ -192,6 +195,26 @@ class CoroutineTransformerMethodVisitor(
} }
} }
// When suspension point is inlined, it is in range of fake inliner variables.
// Path from TABLESWITCH into unspilling goes to latter part of the range.
// In this case the variables are uninitialized, initialize them
private fun initializeFakeInlinerVariables(methodNode: MethodNode, stateLabels: List<LabelNode>) {
for (stateLabel in stateLabels) {
val unitializedFakeInlinerVariables = arrayListOf<LocalVariableNode>()
for (record in methodNode.localVariables) {
if (isFakeLocalVariableForInline(record.name) &&
methodNode.instructions.indexOf(record.start) < methodNode.instructions.indexOf(stateLabel) &&
methodNode.instructions.indexOf(stateLabel) < methodNode.instructions.indexOf(record.end)
) {
methodNode.instructions.insert(stateLabel, withInstructionAdapter {
iconst(0)
store(record.index, Type.INT_TYPE)
})
}
}
}
}
private fun addCompletionParameterToLVT(methodNode: MethodNode) { private fun addCompletionParameterToLVT(methodNode: MethodNode) {
val index = val index =
/* all args */ Type.getMethodType(methodNode.desc).argumentTypes.fold(0) { a, b -> a + b.size } + /* all args */ Type.getMethodType(methodNode.desc).argumentTypes.fold(0) { a, b -> a + b.size } +
@@ -819,14 +842,14 @@ class CoroutineTransformerMethodVisitor(
return suspensionCallEnd.next as LabelNode return suspensionCallEnd.next as LabelNode
} }
private fun transformCallAndReturnContinuationLabel( private fun transformCallAndReturnStateLabel(
id: Int, id: Int,
suspension: SuspensionPoint, suspension: SuspensionPoint,
methodNode: MethodNode, methodNode: MethodNode,
suspendMarkerVarIndex: Int, suspendMarkerVarIndex: Int,
suspendPointLineNumber: LineNumberNode? suspendPointLineNumber: LineNumberNode?
): LabelNode { ): LabelNode {
val continuationLabel = LabelNode().linkWithLabel() val stateLabel = LabelNode().linkWithLabel()
val continuationLabelAfterLoadedResult = LabelNode() val continuationLabelAfterLoadedResult = LabelNode()
val suspendElementLineNumber = lineNumber val suspendElementLineNumber = lineNumber
var nextLineNumberNode = nextDefinitelyHitLineNumber(suspension) var nextLineNumberNode = nextDefinitelyHitLineNumber(suspension)
@@ -854,7 +877,7 @@ class CoroutineTransformerMethodVisitor(
load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE) load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE)
areturn(AsmTypes.OBJECT_TYPE) areturn(AsmTypes.OBJECT_TYPE)
// Mark place for continuation // Mark place for continuation
visitLabel(continuationLabel.label) visitLabel(stateLabel.label)
}) })
// After suspension point there is always three nodes: L1, NOP, L2 // After suspension point there is always three nodes: L1, NOP, L2
@@ -905,7 +928,7 @@ class CoroutineTransformerMethodVisitor(
} }
} }
return continuationLabel return stateLabel
} }
// Find the next line number instruction that is defintely hit. That is, a line number // Find the next line number instruction that is defintely hit. That is, a line number
@@ -1175,7 +1198,7 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
} }
for (variable in oldLvt) { for (variable in oldLvt) {
// $completion, $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables // $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables
// For example, $continuation is used to create async stack trace // For example, $continuation is used to create async stack trace
if (variable.name == CONTINUATION_VARIABLE_NAME || if (variable.name == CONTINUATION_VARIABLE_NAME ||
variable.name == SUSPEND_CALL_RESULT_NAME || variable.name == SUSPEND_CALL_RESULT_NAME ||
@@ -17,7 +17,8 @@ suspend fun ApplicationCall.test(authenticationService: AuthenticationService) {
respond(authenticationService.execute(receiveJSON())) respond(authenticationService.execute(receiveJSON()))
} }
// 2 ISTORE 5 // $i$f$respond x1, $i$f$receiveJSON x2: before and after suspension point
// 3 ISTORE 5
// 0 ILOAD 5 // 0 ILOAD 5
// 1 \$i\$f\$receiveJSON I .* 5 // 1 \$i\$f\$receiveJSON I .* 5
// 1 \$i\$f\$respond I .* 5 // 1 \$i\$f\$respond I .* 5