[JVM] Implement new inlined variable naming format

^KT-65478 fixed
This commit is contained in:
Nikita Nazarov
2023-09-18 16:27:46 +02:00
committed by Alexander Udalov
parent 9ea775cbed
commit 407448d8e3
104 changed files with 88125 additions and 92 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.inline.InlineScopesGenerator
import org.jetbrains.kotlin.codegen.inline.NameGenerator
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putReifiedOperationMarker
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind
@@ -36,6 +37,8 @@ interface BaseExpressionCodegen {
val lastLineNumber: Int
val inlineScopesGenerator: InlineScopesGenerator?
fun propagateChildReifiedTypeParametersUsages(reifiedTypeParametersUsages: ReifiedTypeParametersUsages)
fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean)
@@ -43,6 +43,7 @@ import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
import org.jetbrains.kotlin.config.ApiVersion;
import org.jetbrains.kotlin.config.JVMAssertionsMode;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
@@ -131,6 +132,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
private final TailRecursionCodegen tailRecursionCodegen;
public final CallGenerator defaultCallGenerator = new CallGenerator.DefaultCallGenerator(this);
private final SwitchCodegenProvider switchCodegenProvider;
private final InlineScopesGenerator inlineScopesGenerator;
private final TypeSystemCommonBackendContext typeSystem;
private final Stack<BlockStackElement> blockStackElements = new Stack<>();
@@ -165,6 +168,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
this.tailRecursionCodegen = new TailRecursionCodegen(context, this, this.v, state);
this.switchCodegenProvider = new SwitchCodegenProvider(this);
this.typeSystem = new ClassicTypeSystemContextImpl(state.getModule().getBuiltIns());
boolean inlineScopesEnabled = state.getConfiguration().getBoolean(JVMConfigurationKeys.USE_INLINE_SCOPES_NUMBERS);
this.inlineScopesGenerator = inlineScopesEnabled ? new InlineScopesGenerator() : null;
}
@Nullable
@@ -1569,6 +1575,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return myLastLineNumber;
}
@Override
public InlineScopesGenerator getInlineScopesGenerator() {
return inlineScopesGenerator;
}
private boolean doFinallyOnReturn(@NotNull Label afterReturnLabel, @NotNull List<TryBlockStackElement> nestedTryBlocksWithoutFinally) {
if(!blockStackElements.isEmpty()) {
BlockStackElement stackElement = blockStackElements.peek();
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.codegen.inline.coroutines.CoroutineTransformer
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapperBase
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
@@ -325,13 +326,25 @@ class AnonymousObjectTransformer(
transformationInfo.capturedLambdasToInline, parentRemapper, isConstructor
)
val reifiedTypeParametersUsages = if (inliningContext.shouldReifyTypeParametersInObjects)
inliningContext.root.inlineMethodReifier.reifyInstructions(sourceNode)
else null
val reifiedTypeParametersUsages =
if (inliningContext.shouldReifyTypeParametersInObjects) {
inliningContext.root.inlineMethodReifier.reifyInstructions(sourceNode)
} else {
null
}
val inlineScopesGenerator =
if (state.configuration.getBoolean(JVMConfigurationKeys.USE_INLINE_SCOPES_NUMBERS)) {
InlineScopesGenerator()
} else {
null
}
val result = MethodInliner(
sourceNode,
parameters,
inliningContext.subInline(transformationInfo.nameGenerator),
inliningContext.subInline(
transformationInfo.nameGenerator,
inlineScopesGenerator = inlineScopesGenerator
),
remapper,
isSameModule,
{ "Transformer for " + transformationInfo.oldClassName },
@@ -98,12 +98,15 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
val info = RootInliningContext(
state, codegen.inlineNameGenerator.subGenerator(jvmSignature.asmMethod.name),
sourceCompiler, sourceCompiler.inlineCallSiteInfo, reifiedTypeInliner, typeParameterMappings
sourceCompiler, sourceCompiler.inlineCallSiteInfo, reifiedTypeInliner, typeParameterMappings,
codegen.inlineScopesGenerator
)
val sourceMapper = sourceCompiler.sourceMapper
val sourceInfo = sourceMapper.sourceInfo!!
val callSite = SourcePosition(codegen.lastLineNumber, sourceInfo.sourceFileName!!, sourceInfo.pathOrCleanFQN)
val lastLineNumber = codegen.lastLineNumber
val callSite = SourcePosition(lastLineNumber, sourceInfo.sourceFileName!!, sourceInfo.pathOrCleanFQN)
info.inlineScopesGenerator?.apply { currentCallSiteLineNumber = lastLineNumber }
val inliner = MethodInliner(
node, parameters, info, FieldRemapper(null, null, parameters), sourceCompiler.isCallInsideSameModuleAsCallee,
{ "Method inlining " + sourceCompiler.callElementText },
@@ -0,0 +1,413 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.tree.LineNumberNode
import org.jetbrains.org.objectweb.asm.tree.LocalVariableNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class InlineScopesGenerator {
var inlinedScopes = 0
var currentCallSiteLineNumber = 0
private class InlineScopeNode(
// The marker variable is only null for the root node
val markerVariable: LocalVariableNode?,
val scopeNumber: Int,
var inlineNesting: Int,
val parent: InlineScopeNode?
) {
var callSiteLineNumber: Int? = null
var surroundingScopeNumber: Int? = null
val variables = mutableListOf<LocalVariableNode>()
val children = mutableListOf<InlineScopeNode>()
val isRoot: Boolean
get() = parent == null
init {
parent?.children?.add(this)
}
}
private abstract inner class VariableRenamer {
abstract fun computeInlineScopeInfo(node: InlineScopeNode)
abstract fun LocalVariableNode.belongsToInlineScope(node: InlineScopeNode): Boolean
open fun shouldSkipVariable(variable: LocalVariableNode): Boolean = false
open fun inlineNesting(): Int = -1
fun renameVariables(methodNode: MethodNode): Int {
val rootNode = computeInlineScopesTree(methodNode)
return renameVariables(rootNode)
}
private fun computeInlineScopesTree(methodNode: MethodNode): InlineScopeNode {
val rootNode = InlineScopeNode(null, 0, inlineNesting(), null)
val localVariables = methodNode.localVariables ?: return rootNode
// Inline function and lambda parameters are introduced before the corresponding inline marker variable,
// so we need to keep track of them to assign to the correct scope later.
val variablesWithNotMatchingDepth = mutableListOf<LocalVariableNode>()
val labelToIndex = methodNode.getLabelToIndexMap()
val sortedVariables = localVariables.sortedBy { labelToIndex[it.start.label] }
var seenInlineScopesNumber = 0
var currentNode = rootNode
for (variable in sortedVariables) {
currentNode = currentNode.findClosestSurroundingScope(variable, labelToIndex)
val name = variable.name
if (isFakeLocalVariableForInline(name)) {
seenInlineScopesNumber += 1
val newNode = InlineScopeNode(variable, seenInlineScopesNumber, currentNode.inlineNesting, currentNode)
computeInlineScopeInfo(newNode)
currentNode = newNode
currentNode.variables.addAll(variablesWithNotMatchingDepth)
variablesWithNotMatchingDepth.clear()
} else if (!currentNode.isRoot || !shouldSkipVariable(variable)) {
if (variable.belongsToInlineScope(currentNode)) {
currentNode.variables.add(variable)
} else {
variablesWithNotMatchingDepth.add(variable)
}
}
}
return rootNode
}
private fun renameVariables(rootNode: InlineScopeNode): Int {
var seenInlineScopesNumber = 0
val nodeStack = mutableListOf<InlineScopeNode>()
nodeStack.addAll(rootNode.children)
while (nodeStack.isNotEmpty()) {
val node = nodeStack.removeLast()
seenInlineScopesNumber += 1
with(node) {
markerVariable!!.name = computeNewVariableName(
markerVariable.name,
scopeNumber + inlinedScopes,
callSiteLineNumber,
surroundingScopeNumber
)
}
for (variable in node.variables) {
variable.name = computeNewVariableName(
variable.name,
node.scopeNumber + inlinedScopes,
null,
null
)
}
nodeStack.addAll(node.children)
}
return seenInlineScopesNumber
}
private fun InlineScopeNode.findClosestSurroundingScope(
variable: LocalVariableNode,
labelToIndex: Map<Label, Int>
): InlineScopeNode {
fun LocalVariableNode.contains(other: LocalVariableNode): Boolean {
val startIndex = labelToIndex[start.label] ?: return false
val endIndex = labelToIndex[end.label] ?: return false
val otherStartIndex = labelToIndex[other.start.label] ?: return false
val otherEndIndex = labelToIndex[other.end.label] ?: return false
return startIndex < otherStartIndex && endIndex >= otherEndIndex
}
var node = this
while (!node.isRoot && !node.markerVariable!!.contains(variable)) {
node = node.parent!!
}
return node
}
}
fun addInlineScopesInfo(node: MethodNode, isRegeneratingAnonymousObject: Boolean) {
val localVariables = node.localVariables
if (localVariables?.isEmpty() == true) {
return
}
val markerVariablesWithoutScopeInfoNum = localVariables.count {
isFakeLocalVariableForInline(it.name) && !it.name.contains(INLINE_SCOPE_NUMBER_SEPARATOR)
}
when {
isRegeneratingAnonymousObject -> {
if (markerVariablesWithoutScopeInfoNum > 0) {
addInlineScopesInfoFromIVSuffixesWhenRegeneratingAnonymousObject(node)
}
}
// When inlining a function its marker variable won't contain any scope numbers yet.
// But if there are more than one marker variable like this, it means that we
// are inlining the code produced by the old compiler versions, where inline scopes
// have not been introduced.
markerVariablesWithoutScopeInfoNum == 1 ->
addInlineScopesInfoFromScopeNumbers(node)
else ->
addInlineScopesInfoFromIVSuffixes(node)
}
}
private fun addInlineScopesInfoFromScopeNumbers(node: MethodNode) {
val renamer = object : VariableRenamer() {
override fun computeInlineScopeInfo(node: InlineScopeNode) {
val name = node.markerVariable!!.name
val scopeNumber = node.scopeNumber
val info = name.getInlineScopeInfo()
node.inlineNesting = info?.scopeNumber ?: 0
node.callSiteLineNumber =
if (scopeNumber == 1) {
currentCallSiteLineNumber
} else {
info?.callSiteLineNumber ?: 0
}
if (name.isInlineLambdaName) {
val surroundingScopeNumber = info?.surroundingScopeNumber
node.surroundingScopeNumber =
when {
// The first encountered inline scope belongs to the lambda, which means
// that its surrounding scope is the function where the lambda is being inlined to.
scopeNumber == 1 -> 0
// Every lambda that is already inlined must have a surrounding scope number.
// If it doesn't, then it means that we are inlining the code compiled by
// the older versions of the Kotlin compiler, where surrounding scope numbers
// haven't been introduced yet.
surroundingScopeNumber != null -> surroundingScopeNumber + inlinedScopes + 1
// This situation shouldn't happen, so add invalid info here
else -> -1
}
}
}
override fun LocalVariableNode.belongsToInlineScope(node: InlineScopeNode): Boolean {
val scopeNumber = name.getInlineScopeInfo()?.scopeNumber
val oldScopeNumberOfCurrentMarkerVariable = node.inlineNesting
if (scopeNumber != null) {
return scopeNumber == oldScopeNumberOfCurrentMarkerVariable
}
return !node.isRoot
}
}
inlinedScopes += renamer.renameVariables(node)
}
private fun addInlineScopesInfoFromIVSuffixes(node: MethodNode) {
val labelToLineNumber = node.getLabelToLineNumberMap()
val renamer = object : VariableRenamer() {
override fun computeInlineScopeInfo(node: InlineScopeNode) {
val variable = node.markerVariable!!
val name = variable.name
val ivDepth = node.inlineNesting
val scopeNumber = node.scopeNumber
node.inlineNesting =
if (name.isInlineLambdaName) {
getInlineDepth(name)
} else {
ivDepth + 1
}
node.callSiteLineNumber =
if (scopeNumber == 1) {
currentCallSiteLineNumber
} else {
// When inlining from the code compiled by the old compiler versions,
// the marker variable will not contain the call site line number.
// In this case we will take the line number of the variable start offset
// as the call site line number.
labelToLineNumber[variable.start.label] ?: 0
}
if (name.isInlineLambdaName) {
node.surroundingScopeNumber = computeSurroundingScopeNumber(node)
}
}
override fun LocalVariableNode.belongsToInlineScope(node: InlineScopeNode): Boolean =
!node.isRoot && getInlineDepth(name) == node.inlineNesting
}
inlinedScopes += renamer.renameVariables(node)
}
private fun addInlineScopesInfoFromIVSuffixesWhenRegeneratingAnonymousObject(node: MethodNode) {
val labelToLineNumber = node.getLabelToLineNumberMap()
// This renamer is slightly different from the one we used when computing inline scopes from the
// $iv suffixes. Here no function is being inlined, so the base depth in $iv suffixes is equal to 0.
// When we meet the first marker variable, it should have its depth equal to 1. Apart from that,
// when calculating call site line numbers, we always pick the line number of the marker variable
// start offset and not rely on the `currentCallSiteLineNumber` field. Also, when computing surrounding
// scope numbers we assign surrounding scope 0 (that represents the top frame) to inline lambda
// marker variables that don't have a surrounding scope.
val renamer = object : VariableRenamer() {
// Here inline nesting means depth in $iv suffixes.
// On contrary with the situation when we are inlining a function,
// here we won't meet a marker variable that represents the method node.
// When we meet the first marker variable, it should have depth equal to 1.
override fun inlineNesting(): Int = 0
override fun shouldSkipVariable(variable: LocalVariableNode): Boolean =
!variable.name.contains(INLINE_FUN_VAR_SUFFIX)
override fun computeInlineScopeInfo(node: InlineScopeNode) {
val variable = node.markerVariable!!
val ivDepth = node.inlineNesting
val name = variable.name
node.inlineNesting =
if (name.isInlineLambdaName) {
getInlineDepth(name)
} else {
ivDepth + 1
}
node.callSiteLineNumber = labelToLineNumber[variable.start.label] ?: 0
if (name.isInlineLambdaName) {
node.surroundingScopeNumber = computeSurroundingScopeNumber(node)
}
}
override fun LocalVariableNode.belongsToInlineScope(node: InlineScopeNode): Boolean =
!node.isRoot && getInlineDepth(name) == node.inlineNesting
}
renamer.renameVariables(node)
}
private fun computeSurroundingScopeNumber(currentNode: InlineScopeNode): Int {
val scopeNumber = currentNode.scopeNumber
val currentIVDepth = currentNode.inlineNesting
if (scopeNumber == 1) {
return 0
}
var surroundingScopeNumber: Int? = null
var node = currentNode.parent
while (node != null && !node.isRoot) {
if (node.inlineNesting == currentIVDepth) {
surroundingScopeNumber = node.scopeNumber
break
}
node = node.parent
}
return surroundingScopeNumber?.plus(inlinedScopes) ?: 0
}
private fun computeNewVariableName(
name: String,
scopeNumber: Int,
callSiteLineNumber: Int?,
surroundingScopeNumber: Int?
): String {
val prefix = name.replace(INLINE_FUN_VAR_SUFFIX, "").dropInlineScopeInfo()
return buildString {
append(prefix)
append(INLINE_SCOPE_NUMBER_SEPARATOR)
append(scopeNumber)
if (callSiteLineNumber != null) {
append(INLINE_SCOPE_NUMBER_SEPARATOR)
append(callSiteLineNumber)
if (surroundingScopeNumber != null) {
append(INLINE_SCOPE_NUMBER_SEPARATOR)
append(surroundingScopeNumber)
}
}
}
}
}
fun updateCallSiteLineNumber(name: String, lineNumberMapping: Map<Int, Int>): String =
updateCallSiteLineNumber(name) { lineNumberMapping[it] ?: it }
fun updateCallSiteLineNumber(name: String, newLineNumber: Int): String =
updateCallSiteLineNumber(name) { newLineNumber }
private fun updateCallSiteLineNumber(name: String, calculate: (Int) -> Int): String {
val (scopeNumber, callSiteLineNumber, surroundingScopeNumber) = name.getInlineScopeInfo() ?: return name
if (callSiteLineNumber == null) {
return name
}
val newLineNumber = calculate(callSiteLineNumber)
if (newLineNumber == callSiteLineNumber) {
return name
}
val newName = name
.dropInlineScopeInfo()
.addScopeInfo(scopeNumber)
.addScopeInfo(newLineNumber)
if (surroundingScopeNumber == null) {
return newName
}
return newName.addScopeInfo(surroundingScopeNumber)
}
internal fun MethodNode.getLabelToIndexMap(): Map<Label, Int> =
instructions.filterIsInstance<LabelNode>()
.withIndex()
.associate { (index, labelNode) ->
labelNode.label to index
}
private fun MethodNode.getLabelToLineNumberMap(): Map<Label, Int> {
val result = mutableMapOf<Label, Int>()
var currentLineNumber = 0
for (insn in instructions) {
if (insn is LineNumberNode) {
currentLineNumber = insn.line
} else if (insn is LabelNode) {
result[insn.label] = currentLineNumber
}
}
return result
}
fun String.addScopeInfo(number: Int): String =
"$this$INLINE_SCOPE_NUMBER_SEPARATOR$number"
private fun getInlineDepth(variableName: String): Int {
var endIndex = variableName.length
var depth = 0
val suffixLen = INLINE_FUN_VAR_SUFFIX.length
while (endIndex >= suffixLen) {
if (variableName.substring(endIndex - suffixLen, endIndex) != INLINE_FUN_VAR_SUFFIX) {
break
}
depth++
endIndex -= suffixLen
}
return depth
}
private val String.isInlineLambdaName: Boolean
get() = startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
@@ -14,9 +14,11 @@ class RootInliningContext(
val sourceCompilerForInline: SourceCompilerForInline,
override val callSiteInfo: InlineCallSiteInfo,
val inlineMethodReifier: ReifiedTypeInliner<*>,
typeParameterMappings: TypeParameterMappings<*>
typeParameterMappings: TypeParameterMappings<*>,
inlineScopesGenerator: InlineScopesGenerator?
) : InliningContext(
null, state, nameGenerator, TypeRemapper.createRoot(typeParameterMappings), null, false
null, state, nameGenerator, TypeRemapper.createRoot(typeParameterMappings),
null, false, inlineScopesGenerator
)
class RegeneratedClassContext(
@@ -28,7 +30,7 @@ class RegeneratedClassContext(
override val callSiteInfo: InlineCallSiteInfo,
override val transformationInfo: TransformationInfo
) : InliningContext(
parent, state, nameGenerator, typeRemapper, lambdaInfo, true
parent, state, nameGenerator, typeRemapper, lambdaInfo, true, null
) {
val continuationBuilders: MutableMap<String, ClassBuilder> = hashMapOf()
}
@@ -39,7 +41,8 @@ open class InliningContext(
val nameGenerator: NameGenerator,
val typeRemapper: TypeRemapper,
val lambdaInfo: LambdaInfo?,
val classRegeneration: Boolean
val classRegeneration: Boolean,
val inlineScopesGenerator: InlineScopesGenerator?
) {
val isInliningLambda
get() = lambdaInfo != null
@@ -85,7 +88,8 @@ open class InliningContext(
if (state.isIrBackend)
false // Do not regenerate objects in lambdas inlined into regenerated objects unless needed for some other reason.
else
classRegeneration
classRegeneration,
inlineScopesGenerator
)
fun subInlineWithClassRegeneration(
@@ -103,7 +107,8 @@ open class InliningContext(
generator: NameGenerator,
additionalTypeMappings: Map<String, String?> = emptyMap(),
lambdaInfo: LambdaInfo? = this.lambdaInfo,
classRegeneration: Boolean = this.classRegeneration
classRegeneration: Boolean = this.classRegeneration,
inlineScopesGenerator: InlineScopesGenerator? = null
): InliningContext {
val isInliningLambda = lambdaInfo != null
return InliningContext(
@@ -114,7 +119,7 @@ open class InliningContext(
//root inline lambda
isInliningLambda && !this.isInliningLambda
),
lambdaInfo, classRegeneration
lambdaInfo, classRegeneration, inlineScopesGenerator
)
}
@@ -156,13 +156,25 @@ class MethodInliner(
val fakeContinuationName = CoroutineTransformer.findFakeContinuationConstructorClassName(node)
val markerShift = calcMarkerShift(parameters, node)
var currentLineNumber = if (isInlineOnlyMethod) sourceMapper.callSite!!.line else -1
val lineNumberMapping = mutableMapOf<Int, Int>()
val lambdaInliner = object : InlineAdapter(remappingMethodAdapter, parameters.argsSizeOnStack, sourceMapper) {
private var transformationInfo: TransformationInfo? = null
private var currentLabel: Label? = null
override fun visitLabel(label: Label?) {
currentLabel = label
super.visitLabel(label)
}
override fun visitLineNumber(line: Int, start: Label) {
if (!isInlineOnlyMethod) {
currentLineNumber = line
}
if (GENERATE_SMAP) {
lineNumberMapping[line] = sourceMapper.mapLineNumber(line)
}
super.visitLineNumber(line, start)
}
@@ -303,9 +315,38 @@ class MethodInliner(
)
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
val inlineScopesGenerator = inliningContext.inlineScopesGenerator
val label = currentLabel
// When regenerating anonymous objects we may inline a crossinline lambda before some
// already inlined functions. For these functions their scope numbers should be incremented.
// We also need to temporarily increment the already inlined scopes number by the number of
// inline marker variables that we have found before the crossinline lambda call to assign
// the scope number for this lambda correctly.
val inlineScopeNumberIncrement =
if (inlineScopesGenerator != null && label != null && isRegeneratingAnonymousObject()) {
incrementScopeNumbersOfVariables(node, label)
} else {
0
}
inlineScopesGenerator?.apply {
inlinedScopes += inlineScopeNumberIncrement
currentCallSiteLineNumber =
if (isInlineOnlyMethod) {
currentLineNumber
} else {
sourceMapper.mapLineNumber(currentLineNumber)
}
}
//TODO add skipped this and receiver
val lambdaResult =
inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift)
inlineScopesGenerator?.apply { inlinedScopes -= inlineScopeNumberIncrement }
result.mergeWithNotChangeInfo(lambdaResult)
result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages)
result.reifiedTypeParametersUsages.mergeAll(info.reifiedTypeParametersUsages)
@@ -386,9 +427,46 @@ class MethodInliner(
node.accept(lambdaInliner)
surroundInvokesWithSuspendMarkersIfNeeded(resultNode)
if (inliningContext.inlineScopesGenerator != null) {
updateCallSiteLineNumbers(resultNode, node, lineNumberMapping)
}
return resultNode
}
private fun updateCallSiteLineNumbers(resultNode: MethodNode, inlinedNode: MethodNode, lineNumberMapping: Map<Int, Int>) {
val inlinedNodeLocalVariables = inlinedNode.localVariables ?: return
val resultNodeLocalVariables = resultNode.localVariables ?: return
if (inlinedNodeLocalVariables.isEmpty() || resultNodeLocalVariables.isEmpty()) {
return
}
val markerVariablesFromInlinedNode = inlinedNodeLocalVariables.filter { isFakeLocalVariableForInline(it.name) }
if (markerVariablesFromInlinedNode.isEmpty()) {
return
}
val markerVariableNamesFromInlinedNode = markerVariablesFromInlinedNode.map { it.name }.toMutableSet()
// When updating the call site line numbers, we need to skip the marker variable of the inlined node - it has
// already been assigned a correct call site line number during inlining. However, when regenerating anonymous objects,
// the inliner copies the bodies of the regenerated methods and no marker variables are introduced during this process.
// So in case with anonymous object regeneration we don't have to skip anything.
if (!isRegeneratingAnonymousObject()) {
val labelToIndex = inlinedNode.getLabelToIndexMap()
val markerVariableOfInlinedNode = markerVariablesFromInlinedNode.sortedBy { labelToIndex[it.start.label] }.first()
markerVariableNamesFromInlinedNode.remove(markerVariableOfInlinedNode.name)
}
for (variable in resultNodeLocalVariables) {
val name = variable.name
if (isFakeLocalVariableForInline(name) && name in markerVariableNamesFromInlinedNode) {
variable.name = updateCallSiteLineNumber(name, lineNumberMapping)
}
}
}
private fun prepareNode(node: MethodNode, finallyDeepShift: Int): MethodNode {
node.instructions.resetLabels()
@@ -414,6 +492,8 @@ class MethodInliner(
node.signature, node.exceptions?.toTypedArray()
)
inliningContext.inlineScopesGenerator?.addInlineScopesInfo(node, isRegeneratingAnonymousObject())
val transformationVisitor = object : InlineMethodInstructionAdapter(transformedNode) {
private val GENERATE_DEBUG_INFO = GENERATE_SMAP && !isInlineOnlyMethod
@@ -485,13 +565,29 @@ class MethodInliner(
val isInlineFunctionMarker = name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION)
val newName = when {
inliningContext.isRoot && !isInlineFunctionMarker -> {
val namePrefix = if (name == AsmUtil.THIS) AsmUtil.INLINE_DECLARATION_SITE_THIS else name
namePrefix + INLINE_FUN_VAR_SUFFIX
if (inliningContext.inlineScopesGenerator != null) {
calculateNewNameUsingScopeNumbers(name)
} else {
calculateNewNameUsingTheOldScheme(name)
}
}
else -> name
}
super.visitLocalVariable(newName, desc, signature, start, end, getNewIndex(index))
}
private fun calculateNewNameUsingScopeNumbers(name: String): String {
if (name.startsWith(AsmUtil.THIS)) {
val scopeNumber = name.getInlineScopeInfo()?.scopeNumber ?: return AsmUtil.INLINE_DECLARATION_SITE_THIS
return "${AsmUtil.INLINE_DECLARATION_SITE_THIS}$INLINE_SCOPE_NUMBER_SEPARATOR$scopeNumber"
}
return name
}
private fun calculateNewNameUsingTheOldScheme(name: String): String {
val namePrefix = if (name == AsmUtil.THIS) AsmUtil.INLINE_DECLARATION_SITE_THIS else name
return namePrefix + INLINE_FUN_VAR_SUFFIX
}
}
node.accept(transformationVisitor)
@@ -1207,4 +1303,55 @@ class MethodInliner(
return result
}
}
private fun isRegeneratingAnonymousObject(): Boolean =
inliningContext.parent is RegeneratedClassContext
}
private fun incrementScopeNumbersOfVariables(node: MethodNode, label: Label): Int {
val localVariables = node.localVariables ?: return 0
if (localVariables.isEmpty()) {
return 0
}
val labelToIndex = node.getLabelToIndexMap()
val currentIndex = labelToIndex[label] ?: return 0
var inlineScopeNumberIncrement = 0
for (variable in localVariables) {
val variableStartIndex = labelToIndex[variable.start.label] ?: continue
if (variableStartIndex < currentIndex && isFakeLocalVariableForInline(variable.name)) {
inlineScopeNumberIncrement += 1
}
if (variableStartIndex > currentIndex) {
variable.name = incrementScopeNumbers(variable.name)
}
}
return inlineScopeNumberIncrement
}
private fun incrementScopeNumbers(name: String): String {
val (scopeNumber, callSiteLineNumber, surroundingScopeNumber) = name.getInlineScopeInfo() ?: return name
return buildString {
append(name.dropInlineScopeInfo())
append(INLINE_SCOPE_NUMBER_SEPARATOR)
append(scopeNumber + 1)
if (callSiteLineNumber != null) {
append(INLINE_SCOPE_NUMBER_SEPARATOR)
append(callSiteLineNumber)
}
if (surroundingScopeNumber != null) {
val resultingSurroundingScopeNumber =
if (surroundingScopeNumber != 0) {
surroundingScopeNumber + 1
} else {
0
}
append(INLINE_SCOPE_NUMBER_SEPARATOR)
append(resultingSurroundingScopeNumber)
}
}
}
@@ -55,6 +55,7 @@ import kotlin.math.min
const val GENERATE_SMAP = true
const val NUMBERED_FUNCTION_PREFIX = "kotlin/jvm/functions/Function"
const val INLINE_FUN_VAR_SUFFIX = "\$iv"
const val INLINE_SCOPE_NUMBER_SEPARATOR = '\\'
internal const val FIRST_FUN_LABEL = "$$$$\$ROOT$$$$$"
const val SPECIAL_TRANSFORMATION_NAME = "\$special"
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.inline
data class InlineScopeInfo(val scopeNumber: Int, val callSiteLineNumber: Int?, val surroundingScopeNumber: Int?)
fun String.dropInlineScopeInfo(): String =
substringBefore(INLINE_SCOPE_NUMBER_SEPARATOR)
fun String.getInlineScopeInfo(): InlineScopeInfo? {
val inlineScopeInfoSuffix = substringAfter(INLINE_SCOPE_NUMBER_SEPARATOR)
val numbers = arrayOf(StringBuilder(), StringBuilder(), StringBuilder())
var currentIndex = 0
for (char in inlineScopeInfoSuffix) {
if (char == INLINE_SCOPE_NUMBER_SEPARATOR) {
if (currentIndex >= numbers.size) {
return null
}
currentIndex += 1
} else {
numbers[currentIndex].append(char)
}
}
val scopeNumber = numbers[0].toString().toIntOrNull() ?: return null
val callSiteLineNumber = numbers[1].toString().toIntOrNull()
val surroundingScopeNumber = numbers[2].toString().toIntOrNull()
return InlineScopeInfo(scopeNumber, callSiteLineNumber, surroundingScopeNumber)
}