JVM: Fix local variable ranges in state machine transform.

When a suspension point is inlined, the inlining local spans
unspilling code where the local slot has not been initialized.

The transformer already inserted initialization code for the local,
however, it did not split the local variable table. Therefore,
the inlining local is defined on instructions where the local
slot has no value (namely the instructions that initialize
the local slot on the unspilling path).

This change adds splitting of the local variable table as well.
When updating to a new version of D8, the uninitialized local
slot showed up. D8 will repair it in this case, but it is
better to not generate such code.
This commit is contained in:
Mads Ager
2021-03-22 12:19:56 +01:00
committed by Ilmir Usmanov
parent 911c24d594
commit 571971c5db
@@ -201,20 +201,38 @@ 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
// In this case the variables are uninitialized, initialize them, and split the local variable
// range so that the local variable is only defined when initialized.
private fun initializeFakeInlinerVariables(methodNode: MethodNode, stateLabels: List<LabelNode>) {
for (stateLabel in stateLabels) {
val newRecords = mutableListOf<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)
) {
val newEnd = record.end
val newStart = LabelNode()
record.end = stateLabel
methodNode.instructions.insert(stateLabel, withInstructionAdapter {
iconst(0)
store(record.index, Type.INT_TYPE)
}.also {
it.add(newStart)
})
newRecords.add(
LocalVariableNode(
record.name,
record.desc,
record.signature,
newStart,
newEnd,
record.index
)
)
}
}
methodNode.localVariables.addAll(newRecords)
}
}