JVM: more correctly track inline lambdas in local slots

The analyzer can now handle the case where a parameter containing an
inline lambda is overwritten with something else. The local variable
remapper still can't handle that, and KT-51950 is caused by a deeper
underlying issue (the slot should't get overwritten in the first place),
but at least the problem is more visible now:

>java.lang.RuntimeException: Trying to access skipped parameter:
>  Lkotlin/jvm/functions/Function1; at 2
This commit is contained in:
pyos
2022-04-27 14:46:43 +02:00
committed by Mikhael Bogdanov
parent 51dbc60722
commit 2f6c80f06f
2 changed files with 22 additions and 45 deletions
@@ -661,38 +661,18 @@ class MethodInliner(
}
private fun markObsoleteInstruction(instructions: InsnList, sources: Array<out Frame<BasicValue>?>): SmartSet<AbstractInsnNode> {
val toDelete = SmartSet.create<AbstractInsnNode>()
for (insn in instructions) {
return instructions.filterIndexedTo(SmartSet.create()) { index, insn ->
// Parameter checks are processed separately
if (insn.isAloadBeforeCheckParameterIsNotNull()) continue
val functionalArgument = getFunctionalArgumentIfExists(insn)
if (functionalArgument is LambdaInfo) {
toDelete.add(insn)
} else {
when (insn.opcode) {
Opcodes.ASTORE -> {
if (sources[instructions.indexOf(insn)]?.top().functionalArgument is LambdaInfo) {
toDelete.add(insn)
}
}
Opcodes.SWAP -> {
if (sources[instructions.indexOf(insn)]?.peek(0).functionalArgument is LambdaInfo ||
sources[instructions.indexOf(insn)]?.peek(1).functionalArgument is LambdaInfo
) {
toDelete.add(insn)
}
}
Opcodes.ALOAD -> {
if (sources[instructions.indexOf(insn)]?.getLocal((insn as VarInsnNode).`var`).functionalArgument is LambdaInfo) {
toDelete.add(insn)
}
}
}
!insn.isAloadBeforeCheckParameterIsNotNull() && when (insn.opcode) {
Opcodes.GETFIELD, Opcodes.GETSTATIC, Opcodes.ALOAD ->
sources[index + 1]?.top().functionalArgument is LambdaInfo
Opcodes.PUTFIELD, Opcodes.PUTSTATIC, Opcodes.ASTORE ->
sources[index]?.top().functionalArgument is LambdaInfo
Opcodes.SWAP ->
sources[index]?.peek(0).functionalArgument is LambdaInfo || sources[index]?.peek(1).functionalArgument is LambdaInfo
else -> false
}
}
return toDelete
}
// Replace ALOAD 0
@@ -953,20 +933,18 @@ class MethodInliner(
return inliningContext.typeRemapper.hasNoAdditionalMapping(owner)
}
internal fun getFunctionalArgumentIfExists(insnNode: AbstractInsnNode): FunctionalArgument? {
internal fun getFunctionalArgumentIfExists(insnNode: FieldInsnNode): FunctionalArgument? {
return when {
insnNode.opcode == Opcodes.ALOAD ->
getFunctionalArgumentIfExists((insnNode as VarInsnNode).`var`)
insnNode is FieldInsnNode && insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
findCapturedField(insnNode, nodeRemapper).functionalArgument
insnNode is FieldInsnNode && inliningContext.root.sourceCompilerForInline.isSuspendLambdaCapturedByOuterObjectOrLambda(insnNode.name) ->
inliningContext.root.sourceCompilerForInline.isSuspendLambdaCapturedByOuterObjectOrLambda(insnNode.name) ->
NonInlineArgumentForInlineSuspendParameter.INLINE_LAMBDA_AS_VARIABLE
else ->
null
}
}
private fun getFunctionalArgumentIfExists(varIndex: Int): FunctionalArgument? {
internal fun getFunctionalArgumentIfExists(varIndex: Int): FunctionalArgument? {
if (varIndex < parameters.argsSizeOnStack) {
return parameters.getParameterByDeclarationSlot(varIndex).functionalArgument
}
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
@@ -63,22 +64,20 @@ val BasicValue?.functionalArgument
get() = (this as? FunctionalArgumentValue)?.functionalArgument
internal class FunctionalArgumentInterpreter(private val inliner: MethodInliner) : BasicInterpreter(Opcodes.API_VERSION) {
override fun newParameterValue(isInstanceMethod: Boolean, local: Int, type: Type): BasicValue =
inliner.getFunctionalArgumentIfExists(local)?.let { FunctionalArgumentValue(it, newValue(type)) } ?: newValue(type)
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
wrapArgumentInValueIfNeeded(insn, super.unaryOperation(insn, value))
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? =
wrapArgumentInValueIfNeeded(insn, super.copyOperation(insn, value))
override fun newOperation(insn: AbstractInsnNode): BasicValue? =
wrapArgumentInValueIfNeeded(insn, super.newOperation(insn))
private fun wrapArgumentInValueIfNeeded(
insn: AbstractInsnNode,
basicValue: BasicValue?
): BasicValue? =
inliner.getFunctionalArgumentIfExists(insn)
?.let { FunctionalArgumentValue(it, basicValue) }
?: basicValue
private fun wrapArgumentInValueIfNeeded(insn: AbstractInsnNode, basicValue: BasicValue?): BasicValue? =
if (insn is FieldInsnNode) // GETFIELD or GETSTATIC
inliner.getFunctionalArgumentIfExists(insn)?.let { FunctionalArgumentValue(it, basicValue) } ?: basicValue
else
basicValue
override fun merge(v: BasicValue?, w: BasicValue?): BasicValue? =
if (v is FunctionalArgumentValue && w is FunctionalArgumentValue && v.functionalArgument == w.functionalArgument) v