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:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user