[JVM] Implement new inlined variable naming format
^KT-65478 fixed
This commit is contained in:
committed by
Alexander Udalov
parent
9ea775cbed
commit
407448d8e3
@@ -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();
|
||||
|
||||
+17
-4
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user