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> { private fun markObsoleteInstruction(instructions: InsnList, sources: Array<out Frame<BasicValue>?>): SmartSet<AbstractInsnNode> {
val toDelete = SmartSet.create<AbstractInsnNode>() return instructions.filterIndexedTo(SmartSet.create()) { index, insn ->
for (insn in instructions) {
// Parameter checks are processed separately // Parameter checks are processed separately
if (insn.isAloadBeforeCheckParameterIsNotNull()) continue !insn.isAloadBeforeCheckParameterIsNotNull() && when (insn.opcode) {
val functionalArgument = getFunctionalArgumentIfExists(insn) Opcodes.GETFIELD, Opcodes.GETSTATIC, Opcodes.ALOAD ->
if (functionalArgument is LambdaInfo) { sources[index + 1]?.top().functionalArgument is LambdaInfo
toDelete.add(insn) Opcodes.PUTFIELD, Opcodes.PUTSTATIC, Opcodes.ASTORE ->
} else { sources[index]?.top().functionalArgument is LambdaInfo
when (insn.opcode) { Opcodes.SWAP ->
Opcodes.ASTORE -> { sources[index]?.peek(0).functionalArgument is LambdaInfo || sources[index]?.peek(1).functionalArgument is LambdaInfo
if (sources[instructions.indexOf(insn)]?.top().functionalArgument is LambdaInfo) { else -> false
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)
}
}
}
} }
} }
return toDelete
} }
// Replace ALOAD 0 // Replace ALOAD 0
@@ -953,20 +933,18 @@ class MethodInliner(
return inliningContext.typeRemapper.hasNoAdditionalMapping(owner) return inliningContext.typeRemapper.hasNoAdditionalMapping(owner)
} }
internal fun getFunctionalArgumentIfExists(insnNode: AbstractInsnNode): FunctionalArgument? { internal fun getFunctionalArgumentIfExists(insnNode: FieldInsnNode): FunctionalArgument? {
return when { return when {
insnNode.opcode == Opcodes.ALOAD -> insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
getFunctionalArgumentIfExists((insnNode as VarInsnNode).`var`)
insnNode is FieldInsnNode && insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
findCapturedField(insnNode, nodeRemapper).functionalArgument findCapturedField(insnNode, nodeRemapper).functionalArgument
insnNode is FieldInsnNode && inliningContext.root.sourceCompilerForInline.isSuspendLambdaCapturedByOuterObjectOrLambda(insnNode.name) -> inliningContext.root.sourceCompilerForInline.isSuspendLambdaCapturedByOuterObjectOrLambda(insnNode.name) ->
NonInlineArgumentForInlineSuspendParameter.INLINE_LAMBDA_AS_VARIABLE NonInlineArgumentForInlineSuspendParameter.INLINE_LAMBDA_AS_VARIABLE
else -> else ->
null null
} }
} }
private fun getFunctionalArgumentIfExists(varIndex: Int): FunctionalArgument? { internal fun getFunctionalArgumentIfExists(varIndex: Int): FunctionalArgument? {
if (varIndex < parameters.argsSizeOnStack) { if (varIndex < parameters.argsSizeOnStack) {
return parameters.getParameterByDeclarationSlot(varIndex).functionalArgument 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.codegen.optimization.nullCheck.isCheckParameterIsNotNull
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.org.objectweb.asm.Opcodes 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.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.MethodNode
@@ -63,22 +64,20 @@ val BasicValue?.functionalArgument
get() = (this as? FunctionalArgumentValue)?.functionalArgument get() = (this as? FunctionalArgumentValue)?.functionalArgument
internal class FunctionalArgumentInterpreter(private val inliner: MethodInliner) : BasicInterpreter(Opcodes.API_VERSION) { 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? = override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
wrapArgumentInValueIfNeeded(insn, super.unaryOperation(insn, value)) 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? = override fun newOperation(insn: AbstractInsnNode): BasicValue? =
wrapArgumentInValueIfNeeded(insn, super.newOperation(insn)) wrapArgumentInValueIfNeeded(insn, super.newOperation(insn))
private fun wrapArgumentInValueIfNeeded( private fun wrapArgumentInValueIfNeeded(insn: AbstractInsnNode, basicValue: BasicValue?): BasicValue? =
insn: AbstractInsnNode, if (insn is FieldInsnNode) // GETFIELD or GETSTATIC
basicValue: BasicValue? inliner.getFunctionalArgumentIfExists(insn)?.let { FunctionalArgumentValue(it, basicValue) } ?: basicValue
): BasicValue? = else
inliner.getFunctionalArgumentIfExists(insn) basicValue
?.let { FunctionalArgumentValue(it, basicValue) }
?: basicValue
override fun merge(v: BasicValue?, w: BasicValue?): BasicValue? = override fun merge(v: BasicValue?, w: BasicValue?): BasicValue? =
if (v is FunctionalArgumentValue && w is FunctionalArgumentValue && v.functionalArgument == w.functionalArgument) v if (v is FunctionalArgumentValue && w is FunctionalArgumentValue && v.functionalArgument == w.functionalArgument) v