From 2f6c80f06fa33988d311641781231e25af8a8759 Mon Sep 17 00:00:00 2001 From: pyos Date: Wed, 27 Apr 2022 14:46:43 +0200 Subject: [PATCH] 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 --- .../kotlin/codegen/inline/MethodInliner.kt | 48 +++++-------------- .../codegen/inline/MethodInlinerUtil.kt | 19 ++++---- 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index 68f46ef7ba1..10c66077340 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -661,38 +661,18 @@ class MethodInliner( } private fun markObsoleteInstruction(instructions: InsnList, sources: Array?>): SmartSet { - val toDelete = SmartSet.create() - - 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 } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt index 779cbcea7b1..20f559de681 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt @@ -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