Support smap creation for functions inlined with IR inliner
This commit is contained in:
@@ -41,12 +41,16 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
AsmUtil.genThrow(codegen.visitor, "java/lang/UnsupportedOperationException", "Call is part of inline cycle: $text")
|
||||
}
|
||||
|
||||
fun compileInline(): SMAPAndMethodNode {
|
||||
return sourceCompiler.compileInlineFunction(jvmSignature).apply {
|
||||
node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun performInline(registerLineNumberAfterwards: Boolean, isInlineOnly: Boolean) {
|
||||
var nodeAndSmap: SMAPAndMethodNode? = null
|
||||
try {
|
||||
nodeAndSmap = sourceCompiler.compileInlineFunction(jvmSignature).apply {
|
||||
node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||
}
|
||||
nodeAndSmap = compileInline()
|
||||
val result = inlineCall(nodeAndSmap, isInlineOnly)
|
||||
leaveTemps()
|
||||
codegen.propagateChildReifiedTypeParametersUsages(result.reifiedTypeParametersUsages)
|
||||
|
||||
+3
-6
@@ -49,12 +49,9 @@ abstract class DefaultArgumentFunctionFactory(open val context: CommonBackendCon
|
||||
type = if (makeNullable) newType.makeNullable() else newType,
|
||||
defaultValue = if (it.defaultValue != null) {
|
||||
original.factory.createExpressionBody(
|
||||
IrErrorExpressionImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
it.type,
|
||||
"Default Stub"
|
||||
)
|
||||
IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.type, "Default Stub").apply {
|
||||
attributeOwnerId = it.defaultValue!!.expression
|
||||
}
|
||||
)
|
||||
} else null,
|
||||
isAssignable = it.defaultValue != null
|
||||
|
||||
+3
-1
@@ -462,7 +462,9 @@ class DefaultParameterCleaner(
|
||||
if (replaceDefaultValuesWithStubs) {
|
||||
if (context.mapping.defaultArgumentsOriginalFunction[declaration.parent as IrFunction] == null) {
|
||||
declaration.defaultValue = context.irFactory.createExpressionBody(
|
||||
IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, declaration.type, "Default Stub")
|
||||
IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, declaration.type, "Default Stub").apply {
|
||||
attributeOwnerId = declaration.defaultValue!!.expression
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
||||
+10
-12
@@ -6,24 +6,19 @@
|
||||
package org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.declarations.copyAttributes
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.util.remapTypes
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
class ProvisionalFunctionExpressionLoweringContext(
|
||||
val outer: ProvisionalFunctionExpressionLoweringContext? = null,
|
||||
val startOffset: Int? = null,
|
||||
val endOffset: Int? = null)
|
||||
val endOffset: Int? = null
|
||||
)
|
||||
class ProvisionalFunctionExpressionLowering :
|
||||
IrElementTransformer<ProvisionalFunctionExpressionLoweringContext>,
|
||||
BodyLoweringPass {
|
||||
@@ -35,7 +30,6 @@ class ProvisionalFunctionExpressionLowering :
|
||||
override fun visitCall(expression: IrCall, data: ProvisionalFunctionExpressionLoweringContext) = super.visitCall(
|
||||
expression,
|
||||
ProvisionalFunctionExpressionLoweringContext(
|
||||
data,
|
||||
expression.startOffset,
|
||||
expression.endOffset
|
||||
)
|
||||
@@ -44,14 +38,18 @@ class ProvisionalFunctionExpressionLowering :
|
||||
override fun visitVariable(declaration: IrVariable, data: ProvisionalFunctionExpressionLoweringContext) = super.visitVariable(
|
||||
declaration,
|
||||
ProvisionalFunctionExpressionLoweringContext(
|
||||
data,
|
||||
declaration.startOffset,
|
||||
declaration.endOffset
|
||||
)
|
||||
)
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: ProvisionalFunctionExpressionLoweringContext): IrExpression {
|
||||
if (expression !is IrReturnableBlock) return super.visitContainerExpression(expression, data)
|
||||
return super.visitContainerExpression(expression, ProvisionalFunctionExpressionLoweringContext())
|
||||
}
|
||||
|
||||
override fun visitFunctionExpression(expression: IrFunctionExpression, data: ProvisionalFunctionExpressionLoweringContext): IrElement {
|
||||
expression.transformChildren(this, ProvisionalFunctionExpressionLoweringContext(data))
|
||||
expression.transformChildren(this, ProvisionalFunctionExpressionLoweringContext())
|
||||
|
||||
val startOffset = data.startOffset ?: expression.startOffset
|
||||
val endOffset = data.endOffset ?: expression.endOffset
|
||||
@@ -70,7 +68,7 @@ class ProvisionalFunctionExpressionLowering :
|
||||
valueArgumentsCount = function.valueParameters.size,
|
||||
reflectionTarget = null,
|
||||
origin = origin
|
||||
)
|
||||
).copyAttributes(expression) // TODO better to copy just name
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+3
-1
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||
@@ -162,7 +163,8 @@ class ReturnableBlockTransformer(val context: CommonBackendContext, val containe
|
||||
if (!expression.type.isUnit()) {
|
||||
// In case of Unit return type we don't need to return an explicit value. This will not be optimized by JVM backend and
|
||||
// may result in exceptions in `MethodVerifier` before optimizations.
|
||||
+irGet(variable)
|
||||
// Also note that `UNDEFINED_OFFSET` is needed to make proper line number for JVM.
|
||||
+at(UNDEFINED_OFFSET, UNDEFINED_OFFSET).irGet(variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-9
@@ -689,6 +689,8 @@ class FunctionInlining(
|
||||
} else {
|
||||
val newVariable =
|
||||
currentScope.scope.createTemporaryVariable(
|
||||
startOffset = if (it.isDefaultArg) irExpression.startOffset else UNDEFINED_OFFSET,
|
||||
endOffset = if (it.isDefaultArg) irExpression.startOffset else UNDEFINED_OFFSET,
|
||||
irExpression = irExpression,
|
||||
irType = if (inlineArgumentsWithTheirOriginalTypeAndOffset) it.parameter.getOriginalType() else irExpression.type,
|
||||
nameHint = callee.symbol.owner.name.asStringStripSpecialMarkers() + "_" + it.parameter.name.asStringStripSpecialMarkers(),
|
||||
@@ -793,14 +795,18 @@ class FunctionInlining(
|
||||
(alwaysCreateTemporaryVariablesForArguments && !parameter.isInlineParameter()) ||
|
||||
argument.shouldBeSubstitutedViaTemporaryVariable()
|
||||
|
||||
if (!shouldCreateTemporaryVariable) {
|
||||
substituteMap[parameter] = variableInitializer
|
||||
if (shouldCreateTemporaryVariable) {
|
||||
val newVariable = createTemporaryVariable(parameter, variableInitializer, argument.isDefaultArg, callee)
|
||||
if (argument.isDefaultArg) evaluationStatementsFromDefault.add(newVariable) else evaluationStatements.add(newVariable)
|
||||
substituteMap[parameter] = IrGetValueWithoutLocation(newVariable.symbol)
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val newVariable = createTemporaryVariable(parameter, variableInitializer, callee)
|
||||
if (argument.isDefaultArg) evaluationStatementsFromDefault.add(newVariable) else evaluationStatements.add(newVariable)
|
||||
substituteMap[parameter] = IrGetValueWithoutLocation(newVariable.symbol)
|
||||
substituteMap[parameter] = if (variableInitializer is IrGetValue) {
|
||||
IrGetValueWithoutLocation(variableInitializer.symbol)
|
||||
} else {
|
||||
variableInitializer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -824,13 +830,19 @@ class FunctionInlining(
|
||||
}
|
||||
|
||||
private fun ParameterToArgument.shouldBeSubstitutedViaTemporaryVariable(): Boolean =
|
||||
!isImmutableVariableLoad && !(argumentExpression.isPure(false, context = context) && inlinePureArguments)
|
||||
!(isImmutableVariableLoad && parameter.index >= 0) &&
|
||||
!(argumentExpression.isPure(false, context = context) && inlinePureArguments)
|
||||
|
||||
private fun createTemporaryVariable(parameter: IrValueParameter, variableInitializer: IrExpression, callee: IrFunction): IrVariable {
|
||||
private fun createTemporaryVariable(
|
||||
parameter: IrValueParameter,
|
||||
variableInitializer: IrExpression,
|
||||
isDefaultArg: Boolean,
|
||||
callee: IrFunction
|
||||
): IrVariable {
|
||||
val variable = currentScope.scope.createTemporaryVariable(
|
||||
irExpression = IrBlockImpl(
|
||||
variableInitializer.startOffset,
|
||||
variableInitializer.endOffset,
|
||||
if (isDefaultArg) variableInitializer.startOffset else UNDEFINED_OFFSET,
|
||||
if (isDefaultArg) variableInitializer.endOffset else UNDEFINED_OFFSET,
|
||||
if (inlineArgumentsWithTheirOriginalTypeAndOffset) parameter.getOriginalType() else variableInitializer.type,
|
||||
InlinerExpressionLocationHint((currentScope.irElement as IrSymbolOwner).symbol)
|
||||
).apply {
|
||||
|
||||
+1
-1
@@ -81,6 +81,7 @@ class ClassCodegen private constructor(
|
||||
)
|
||||
}
|
||||
|
||||
val smap = context.getSourceMapper(irClass)
|
||||
private val state get() = context.state
|
||||
|
||||
private val innerClasses = mutableSetOf<IrClass>()
|
||||
@@ -158,7 +159,6 @@ class ClassCodegen private constructor(
|
||||
// Generating a method node may cause the addition of a field with an initializer if an inline function
|
||||
// call uses `assert` and the JVM assertions mode is enabled. To avoid concurrent modification errors,
|
||||
// there is a very specific generation order.
|
||||
val smap = context.getSourceMapper(irClass)
|
||||
// 1. Any method other than `<clinit>` can add a field and a `<clinit>` statement:
|
||||
for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
|
||||
if (method.name.asString() != "<clinit>" &&
|
||||
|
||||
+316
-12
@@ -5,8 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.*
|
||||
import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER
|
||||
import org.jetbrains.kotlin.backend.common.lower.LoweredStatementOrigins
|
||||
import org.jetbrains.kotlin.backend.common.lower.inline.isAdaptedFunctionReference
|
||||
import org.jetbrains.kotlin.backend.jvm.*
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IntrinsicMethod
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
|
||||
@@ -34,6 +36,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
@@ -54,12 +57,15 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
import org.jetbrains.kotlin.types.computeExpandedTypeForInlineClass
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.util.*
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
sealed class ExpressionInfo {
|
||||
var blockInfo: BlockInfo? = null
|
||||
@@ -138,6 +144,21 @@ class ExpressionCodegen(
|
||||
val smap: SourceMapper,
|
||||
val reifiedTypeParametersUsages: ReifiedTypeParametersUsages,
|
||||
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
||||
data class AdditionalIrInlineData(val smap: SourceMapCopier, val inlinedBlock: IrInlinedFunctionBlock, val parentSmap: SourceMapper, val tryInfo: TryWithFinallyInfo?) {
|
||||
// fun isInvokeOnLambda(): Boolean = inlinedBlock.inlineCall.symbol.owner.name == OperatorNameConventions.INVOKE
|
||||
}
|
||||
|
||||
val classToCachedSourceMapper = mutableMapOf<IrDeclaration, SourceMapper>()
|
||||
val localSmapCopiersByClass = mutableListOf<AdditionalIrInlineData>()
|
||||
|
||||
private fun getLocalSmap(): List<AdditionalIrInlineData> = /*context.*/localSmapCopiersByClass
|
||||
private fun dropLastLocalSmap() {
|
||||
/*context.*/localSmapCopiersByClass.removeLast()
|
||||
}
|
||||
|
||||
private fun addToLocalSmap(info: AdditionalIrInlineData) {
|
||||
/*context.*/localSmapCopiersByClass.add(info)
|
||||
}
|
||||
|
||||
override fun toString(): String = signature.toString()
|
||||
|
||||
@@ -152,7 +173,7 @@ class ExpressionCodegen(
|
||||
|
||||
val state = context.state
|
||||
|
||||
private val fileEntry = irFunction.fileParent.fileEntry
|
||||
private val fileEntry = irFunction.fileParentBeforeInline.fileEntry
|
||||
|
||||
override val visitor: InstructionAdapter
|
||||
get() = mv
|
||||
@@ -187,7 +208,35 @@ class ExpressionCodegen(
|
||||
|
||||
private fun markNewLinkedLabel() = linkedLabel().apply { mv.visitLabel(this) }
|
||||
|
||||
private fun getLineNumberForOffset(offset: Int): Int = fileEntry.getLineNumber(offset) + 1
|
||||
private fun getLineNumberForOffset(offset: Int): Int {
|
||||
if (getLocalSmap().isNotEmpty()) {
|
||||
var previousData: AdditionalIrInlineData? = null
|
||||
var result = -1
|
||||
val iterator = getLocalSmap().reversed().iterator()
|
||||
while (iterator.hasNext()) {
|
||||
if (previousData?.inlinedBlock?.isLambdaInlining() == true) {
|
||||
while (iterator.hasNext() && iterator.next().inlinedBlock.inlineDeclaration != getInlinedAt(previousData.inlinedBlock.inlinedElement)) {
|
||||
// after lambda's smap we should skip "frames" that were inlined inside body of inline function that accept given lambda
|
||||
continue
|
||||
}
|
||||
if (!iterator.hasNext()) break
|
||||
}
|
||||
val inlineData = iterator.next()
|
||||
|
||||
previousData = inlineData
|
||||
val localFileEntry = if (inlineData.inlinedBlock.inlinedElement is IrCallableReference<*>)
|
||||
getLocalSmap().reversed().map { it.inlinedBlock }
|
||||
.firstOrNull { it.isFunctionInlining() }?.inlineDeclaration?.fileOrNull?.fileEntry ?: fileEntry
|
||||
else
|
||||
inlineData.inlinedBlock.inlineDeclaration.fileEntry
|
||||
val lineNumber = if (result == -1) localFileEntry.getLineNumber(offset) + 1 else result
|
||||
val mappedLineNumber = inlineData.smap.mapLineNumber(lineNumber)
|
||||
result = mappedLineNumber
|
||||
}
|
||||
return result
|
||||
}
|
||||
return fileEntry.getLineNumber(offset) + 1
|
||||
}
|
||||
|
||||
private fun IrElement.markLineNumber(startOffset: Boolean) {
|
||||
if (noLineNumberScope) return
|
||||
@@ -195,6 +244,7 @@ class ExpressionCodegen(
|
||||
if (offset < 0) return
|
||||
|
||||
val lineNumber = getLineNumberForOffset(offset)
|
||||
|
||||
assert(lineNumber > 0)
|
||||
if (lastLineNumber != lineNumber) {
|
||||
lastLineNumber = lineNumber
|
||||
@@ -204,6 +254,17 @@ class ExpressionCodegen(
|
||||
|
||||
fun markLineNumber(element: IrElement) = element.markLineNumber(true)
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private inline fun noLineNumberScopeWithCondition(flag: Boolean, block: () -> Unit) {
|
||||
contract {
|
||||
callsInPlace(block, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
val previousState = noLineNumberScope
|
||||
noLineNumberScope = noLineNumberScope || flag
|
||||
block()
|
||||
noLineNumberScope = previousState
|
||||
}
|
||||
|
||||
fun noLineNumberScope(block: () -> Unit) {
|
||||
val previousState = noLineNumberScope
|
||||
noLineNumberScope = true
|
||||
@@ -400,6 +461,11 @@ class ExpressionCodegen(
|
||||
writeLocalVariablesInTable(info, markNewLabel())
|
||||
}
|
||||
}
|
||||
|
||||
if (expression is IrInlinedFunctionBlock && expression.isFunctionInlining()) {
|
||||
markLineNumberAfterInlineIfNeeded(isInsideCondition)
|
||||
}
|
||||
|
||||
if (isSynthesizedInitBlock) {
|
||||
expression.markLineNumber(startOffset = false)
|
||||
mv.nop()
|
||||
@@ -453,11 +519,138 @@ class ExpressionCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) =
|
||||
container.statements.fold(unitValue) { prev, exp ->
|
||||
private inline fun stashSmapForGivenTry(tryInfo: TryWithFinallyInfo, block: () -> Unit) {
|
||||
val lastLineNumberBeforeFinally = lastLineNumber
|
||||
val localSmap = getLocalSmap()
|
||||
val smapCountToDrop = localSmap.indexOfFirst { it.tryInfo == tryInfo }
|
||||
if (smapCountToDrop == -1) {
|
||||
return block()
|
||||
}
|
||||
|
||||
val smapInTryBlock = localSmap.takeLast(localSmap.size - smapCountToDrop)
|
||||
smapInTryBlock.forEach { _ -> dropLastLocalSmap() }
|
||||
block()
|
||||
smapInTryBlock.forEach { addToLocalSmap(it) }
|
||||
if (smapInTryBlock.isNotEmpty()) {
|
||||
lastLineNumber = lastLineNumberBeforeFinally
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitInlinedFunctionBlock(block: IrInlinedFunctionBlock, data: BlockInfo): PromisedValue {
|
||||
val lineNumberForOffset = getLineNumberForOffset(block.inlineCall.startOffset)
|
||||
val callee = block.inlineDeclaration as? IrFunction
|
||||
|
||||
block.getNonDefaultAdditionalStatementsFromInlinedBlock().forEach { exp ->
|
||||
exp.accept(this, data).discard()
|
||||
}
|
||||
|
||||
// TODO start_4: reuse code from org/jetbrains/kotlin/codegen/inline/MethodInliner.kt:267
|
||||
if (block.isLambdaInlining()) {
|
||||
val overrideLineNumber = getLocalSmap().reversed().firstOrNull { !it.inlinedBlock.isLambdaInlining() }
|
||||
?.inlinedBlock?.inlineDeclaration?.isInlineOnly() == true
|
||||
val currentLineNumber = if (overrideLineNumber) getLocalSmap().last().smap.callSite!!.line else lineNumberForOffset
|
||||
|
||||
val firstLine = callee?.body?.statements?.firstOrNull()?.let {
|
||||
block.inlineDeclaration.fileEntry.getLineNumber(it.startOffset) + 1
|
||||
} ?: -1
|
||||
// TODO DefaultLambda
|
||||
if (/*(info is DefaultLambda != overrideLineNumber) &&*/ currentLineNumber >= 0 && firstLine == currentLineNumber) {
|
||||
val label = Label()
|
||||
val fakeLineNumber = (getLocalSmap().lastOrNull()?.smap?.parent ?: smap)
|
||||
.mapSyntheticLineNumber(SourceMapper.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER)
|
||||
mv.visitLabel(label)
|
||||
mv.visitLineNumber(fakeLineNumber, label)
|
||||
}
|
||||
}
|
||||
// TODO end_4
|
||||
|
||||
noLineNumberScopeWithCondition(block.inlineDeclaration.isInlineOnly()) {
|
||||
buildSmapFor(block, data)
|
||||
|
||||
block.getDefaultAdditionalStatementsFromInlinedBlock().forEach { exp ->
|
||||
exp.accept(this, data).discard()
|
||||
}
|
||||
|
||||
if (block.inlineCall.usesDefaultArguments()) {
|
||||
// we must reset LN because at this point in original inliner we will inline non default call
|
||||
lastLineNumber = -1
|
||||
}
|
||||
|
||||
val result = block.getOriginalStatementsFromInlinedBlock().fold(unitValue) { prev, exp ->
|
||||
prev.discard()
|
||||
exp.accept(this, data)
|
||||
}
|
||||
|
||||
val calleeBody = callee?.body
|
||||
if (block.inlinedElement !is IrCallableReference<*> || callee?.isInline == true) {
|
||||
if (calleeBody !is IrStatementContainer || calleeBody.statements.lastOrNull() !is IrReturn) {
|
||||
// TODO start_1: reuse code
|
||||
// Allow setting a breakpoint on the closing brace of a void-returning function
|
||||
// without an explicit return, or the `class Something(` line of a primary constructor.
|
||||
if (callee?.origin != JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER) {
|
||||
callee?.let {
|
||||
it.markLineNumber(startOffset = callee is IrConstructor && callee.isPrimary)
|
||||
mv.nop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO end_1
|
||||
|
||||
// if (!(block.origin as InlinedFunction).isLambdaInlining) {
|
||||
// TODO start_3: reuse from visitReturn and see ReturnableBlockLowering
|
||||
val lastStatement = callee?.body?.statements?.lastOrNull()
|
||||
if (lastStatement is IrReturn) {
|
||||
val returnTarget = lastStatement.returnTargetSymbol.owner
|
||||
val originalTarget = (returnTarget as? IrAttributeContainer)?.attributeOwnerId ?: returnTarget
|
||||
if (originalTarget == block.inlineDeclaration) {
|
||||
// if return is implicit we must put new LN at the end of expression
|
||||
block.statements.last().markLineNumber(startOffset = lastStatement.startOffset != lastStatement.endOffset)
|
||||
mv.nop()
|
||||
}
|
||||
}
|
||||
// TODO end_3
|
||||
// }
|
||||
|
||||
dropLastLocalSmap()
|
||||
|
||||
|
||||
if (block.isLambdaInlining()) {
|
||||
val overrideLineNumber = getLocalSmap().reversed().firstOrNull { !it.inlinedBlock.isLambdaInlining() }
|
||||
?.inlinedBlock?.inlineDeclaration?.isInlineOnly() == true
|
||||
val currentLineNumber = if (overrideLineNumber) getLocalSmap().last().smap.callSite!!.line else lineNumberForOffset
|
||||
// currentLineNumber = getLineNumberForOffset(marker.inlineCall.startOffset)
|
||||
// TODO start_2: reuse code
|
||||
if (currentLineNumber != -1) {
|
||||
if (overrideLineNumber) {
|
||||
// This is from the function we're inlining into, so no need to remap.
|
||||
mv.visitLineNumber(currentLineNumber, markNewLabel())
|
||||
} else {
|
||||
// Need to go through the superclass here to properly remap the line number via `sourceMapper`.
|
||||
block.inlineCall.markLineNumber(true)
|
||||
}
|
||||
mv.nop()
|
||||
}
|
||||
// TODO end_2
|
||||
}
|
||||
|
||||
// takeUnless is required to avoid markLineNumberAfterInlineIfNeeded for inline only
|
||||
if (block.isFunctionInlining()) {
|
||||
lastLineNumber = lineNumberForOffset.takeUnless { noLineNumberScope } ?: -1
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo): PromisedValue {
|
||||
if (container is IrInlinedFunctionBlock) {
|
||||
return visitInlinedFunctionBlock(container, data)
|
||||
}
|
||||
return container.statements.fold(unitValue) { prev, exp ->
|
||||
prev.discard()
|
||||
exp.accept(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): PromisedValue {
|
||||
visitStatementContainer(body, data).discard()
|
||||
@@ -856,11 +1049,116 @@ class ExpressionCodegen(
|
||||
element.render()
|
||||
)
|
||||
|
||||
private fun IrDeclaration.getClassWithDeclaredFunction(): IrClass? {
|
||||
val parent = this.parentClassOrNull ?: return null
|
||||
if (!parent.isInterface || (this is IrFunction && this.hasJvmDefault())) return parent
|
||||
return parent.declarations.singleOrNull { it.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS } as IrClass
|
||||
}
|
||||
|
||||
private fun IrFunctionAccessExpression.isInvokeOnDefaultArg(expected: IrDeclaration): Boolean {
|
||||
if (this.symbol.owner.name != OperatorNameConventions.INVOKE) return false
|
||||
|
||||
val dispatch = this.dispatchReceiver as? IrGetValue
|
||||
val parameter = dispatch?.symbol?.owner as? IrValueParameter
|
||||
val default = parameter?.defaultValue?.expression as? IrFunctionExpression
|
||||
|
||||
return default?.function == expected
|
||||
}
|
||||
|
||||
private fun getInlinedAt(originalExpression: IrElement): IrDeclaration? {
|
||||
for (block in getLocalSmap().map { it.inlinedBlock }.filter { it.isFunctionInlining() }) {
|
||||
block.inlineCall.getAllArgumentsWithIr().forEach {
|
||||
val actualArg = (it.second?.attributeOwnerId ?: ((it.first.defaultValue?.expression?.attributeOwnerId as? IrBlock)?.statements?.firstOrNull() as? IrClass)?.attributeOwnerId) as? IrExpression
|
||||
val extractedAnonymousFunction = if (actualArg?.isAdaptedFunctionReference() == true) ((actualArg as IrBlock).statements.last() as IrFunctionReference).attributeOwnerId else actualArg
|
||||
if (extractedAnonymousFunction == ((originalExpression as? IrAttributeContainer)?.attributeOwnerId ?: originalExpression)) {
|
||||
return block.inlineDeclaration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun buildSmapFor(declaration: IrInlinedFunctionBlock, data: BlockInfo): PromisedValue {
|
||||
val inlineCall = declaration.inlineCall
|
||||
val callee = declaration.inlineDeclaration
|
||||
|
||||
declaration.inlineCall.markLineNumber(startOffset = true)
|
||||
mv.nop()
|
||||
|
||||
val localSmaps = getLocalSmap()
|
||||
if (declaration.isLambdaInlining()) {
|
||||
val callSite = getLocalSmap().lastOrNull()?.smap?.callSite?.takeIf { inlineCall.isInvokeOnDefaultArg(callee) }
|
||||
val classSMAP = context.typeToCachedSMAP[context.getLocalClassType(declaration.inlinedElement as IrAttributeContainer)]!!
|
||||
|
||||
val sourceMapper = if (localSmaps.isEmpty()) {
|
||||
smap
|
||||
} else {
|
||||
localSmaps.reversed().firstOrNull { it.inlinedBlock.inlineDeclaration == getInlinedAt(declaration.inlinedElement) }?.smap?.parent
|
||||
?: localSmaps.last().smap.parent // if we are in anonymous inlined class and lambda was declared outside
|
||||
}
|
||||
addToLocalSmap(
|
||||
AdditionalIrInlineData(
|
||||
SourceMapCopier(sourceMapper, classSMAP, callSite),
|
||||
declaration,
|
||||
context.getSourceMapper(if (declaration.inlinedElement is IrCallableReference<*>) irFunction.parentAsClass else callee.parentClassOrNull!!),
|
||||
data.infos.filterIsInstance<TryWithFinallyInfo>().lastOrNull()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val calleeFromActualClass = callee.getClassWithDeclaredFunction()!!.declarations
|
||||
.asSequence()
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.filter { it.attributeOwnerId == callee } // original callee could be transformed after lowerings, so we must get correct one
|
||||
.filter { if (inlineCall.usesDefaultArguments()) it.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER else it.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER }
|
||||
.filter { it.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE } // filter functions with $$forInline postfix
|
||||
.single()
|
||||
val nodeAndSmap = calleeFromActualClass.let { actualCallee ->
|
||||
val callToActualCallee = IrCallImpl.fromSymbolOwner(inlineCall.startOffset, inlineCall.endOffset, inlineCall.type, actualCallee.symbol)
|
||||
val callable = methodSignatureMapper.mapToCallableMethod(callToActualCallee, null)
|
||||
val callGenerator = getOrCreateCallGenerator(callToActualCallee, data, callable.signature)
|
||||
(callGenerator as InlineCodegen<*>).compileInline()
|
||||
}
|
||||
|
||||
val key = callee.parentClassOrNull!!
|
||||
val newSmap = if (localSmaps.isEmpty()) {
|
||||
smap
|
||||
} else {
|
||||
localSmaps.last().parentSmap
|
||||
}
|
||||
val sourcePosition = let {
|
||||
val sourceInfo = newSmap.sourceInfo!!
|
||||
val localFileEntry = getLocalSmap().lastOrNull()?.inlinedBlock?.inlineDeclaration?.fileEntry ?: fileEntry
|
||||
val line = if (inlineCall.startOffset < 0) lastLineNumber else localFileEntry.getLineNumber(inlineCall.startOffset) + 1
|
||||
// val file = fileEntry.name.drop(1)
|
||||
SourcePosition(line, sourceInfo.sourceFileName!!, sourceInfo.pathOrCleanFQN)
|
||||
}
|
||||
val sourceMapCopier = SourceMapCopier(newSmap, nodeAndSmap.classSMAP, sourcePosition)
|
||||
addToLocalSmap(
|
||||
AdditionalIrInlineData(
|
||||
sourceMapCopier, declaration, context.getSourceMapper(key),
|
||||
data.infos.filterIsInstance<TryWithFinallyInfo>().lastOrNull()
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
if (inlineCall.usesDefaultArguments()) {
|
||||
// $default function has first LN pointing to original callee
|
||||
callee.markLineNumber(startOffset = true)
|
||||
mv.nop()
|
||||
}
|
||||
|
||||
return unitValue
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass, data: BlockInfo): PromisedValue {
|
||||
if (declaration.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS) {
|
||||
val childCodegen = ClassCodegen.getOrCreate(declaration, context, enclosingFunctionForLocalObjects)
|
||||
childCodegen.generate()
|
||||
closureReifiedMarkers[declaration] = childCodegen.reifiedTypeParametersUsages
|
||||
if (irFunction.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || declaration.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL) {
|
||||
context.typeToCachedSMAP[childCodegen.type] = SMAP(childCodegen.smap.resultMappings)
|
||||
}
|
||||
}
|
||||
return unitValue
|
||||
}
|
||||
@@ -1169,6 +1467,10 @@ class ExpressionCodegen(
|
||||
mv.nop()
|
||||
val endLabel = Label()
|
||||
val stackElement = unwindBlockStack(endLabel, data) { it.loop == jump.loop }
|
||||
if ((jump.loop.body as? IrBlock)?.statements?.singleOrNull() is IrInlinedFunctionBlock) {
|
||||
// There must be another line number because this jump is actually return from inlined function
|
||||
jump.markLineNumber(startOffset = true)
|
||||
}
|
||||
if (stackElement == null) {
|
||||
generateGlobalReturnFlagIfPossible(jump, jump.loop.nonLocalReturnLabel(jump is IrBreak))
|
||||
mv.areturn(Type.VOID_TYPE)
|
||||
@@ -1307,15 +1609,17 @@ class ExpressionCodegen(
|
||||
) {
|
||||
val gapStart = markNewLinkedLabel()
|
||||
data.localGapScope(tryWithFinallyInfo) {
|
||||
finallyDepth++
|
||||
if (isFinallyMarkerRequired) {
|
||||
generateFinallyMarker(mv, finallyDepth, true)
|
||||
stashSmapForGivenTry(tryWithFinallyInfo) {
|
||||
finallyDepth++
|
||||
if (isFinallyMarkerRequired) {
|
||||
generateFinallyMarker(mv, finallyDepth, true)
|
||||
}
|
||||
tryWithFinallyInfo.onExit.accept(this, data).discard()
|
||||
if (isFinallyMarkerRequired) {
|
||||
generateFinallyMarker(mv, finallyDepth, false)
|
||||
}
|
||||
finallyDepth--
|
||||
}
|
||||
tryWithFinallyInfo.onExit.accept(this, data).discard()
|
||||
if (isFinallyMarkerRequired) {
|
||||
generateFinallyMarker(mv, finallyDepth, false)
|
||||
}
|
||||
finallyDepth--
|
||||
if (tryCatchBlockEnd != null) {
|
||||
tryWithFinallyInfo.onExit.markLineNumber(startOffset = false)
|
||||
mv.goTo(tryCatchBlockEnd)
|
||||
|
||||
+8
-16
@@ -25,8 +25,8 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import kotlin.collections.set
|
||||
|
||||
class IrFrameMap : FrameMapBase<IrSymbol>() {
|
||||
private val typeMap = mutableMapOf<IrSymbol, Type>()
|
||||
@@ -67,19 +68,11 @@ fun IrFrameMap.leave(irDeclaration: IrSymbolOwner): Int {
|
||||
return leave(irDeclaration.symbol)
|
||||
}
|
||||
|
||||
fun IrClass.getOriginalDeclaration(): IrDeclaration? {
|
||||
val originalClass = this.attributeOwnerIdBeforeInline ?: return null
|
||||
return when (originalClass) {
|
||||
is IrClass -> return originalClass
|
||||
is IrFunctionExpression -> originalClass.function
|
||||
is IrFunctionReference -> originalClass.symbol.owner
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun JvmBackendContext.getSourceMapper(declaration: IrClass): SourceMapper {
|
||||
val originalDeclarationBeforeInline = declaration.getOriginalDeclaration() ?: declaration
|
||||
val fileEntry = originalDeclarationBeforeInline.fileParent.fileEntry
|
||||
val irFile = declaration.fileParentBeforeInline
|
||||
val type = declaration.getAttributeOwnerBeforeInline()?.let { getLocalClassType(it) } ?: defaultTypeMapper.mapClass(declaration)
|
||||
|
||||
val fileEntry = irFile.fileEntry
|
||||
// NOTE: apparently inliner requires the source range to cover the
|
||||
// whole file the class is declared in rather than the class only.
|
||||
val endLineNumber = when (fileEntry) {
|
||||
@@ -88,9 +81,8 @@ fun JvmBackendContext.getSourceMapper(declaration: IrClass): SourceMapper {
|
||||
}
|
||||
val sourceFileName = when (fileEntry) {
|
||||
is MultifileFacadeFileEntry -> fileEntry.partFiles.singleOrNull()?.name
|
||||
else -> originalDeclarationBeforeInline.fileParent.name
|
||||
else -> irFile.name
|
||||
}
|
||||
val type = declaration.attributeOwnerIdBeforeInline?.let { getLocalClassType(it)!! } ?: defaultTypeMapper.mapClass(declaration)
|
||||
return SourceMapper(
|
||||
SourceInfo(
|
||||
sourceFileName,
|
||||
|
||||
+1
@@ -17,6 +17,7 @@ object SingleArgumentInlineFunctionIntrinsic : IntrinsicMethod() {
|
||||
if (inlineLambda != null) {
|
||||
val lambdaInfo = IrExpressionLambdaImpl(codegen, inlineLambda)
|
||||
lambdaInfo.generateLambdaBody(sourceCompiler)
|
||||
codegen.context.typeToCachedSMAP[lambdaInfo.lambdaClassType] = lambdaInfo.node.classSMAP
|
||||
}
|
||||
|
||||
return codegen.unitValue
|
||||
|
||||
+1
-1
@@ -209,7 +209,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
||||
}
|
||||
|
||||
this.metadata = function.metadata
|
||||
context.suspendLambdaToOriginalFunctionMap[attributeOwnerId as IrFunctionReference] = function
|
||||
context.suspendLambdaToOriginalFunctionMap[attributeOwnerId] = function
|
||||
}
|
||||
|
||||
private fun IrClass.addInvokeSuspendForLambda(
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.backend.jvm.caches.BridgeLoweringCache
|
||||
import org.jetbrains.kotlin.backend.jvm.caches.CollectionStubComputer
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrTypeMapper
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.MethodSignatureMapper
|
||||
import org.jetbrains.kotlin.codegen.inline.SMAP
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
@@ -29,7 +30,6 @@ import org.jetbrains.kotlin.ir.builders.irBlock
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
@@ -105,6 +105,9 @@ class JvmBackendContext(
|
||||
|
||||
lateinit var getIntrinsic: (IrFunctionSymbol) -> IntrinsicMarker?
|
||||
|
||||
// Store evaluated SMAP for anonymous classes. Used only with IR inliner.
|
||||
val typeToCachedSMAP = mutableMapOf<Type, SMAP>()
|
||||
|
||||
private val localClassType = ConcurrentHashMap<IrAttributeContainer, Type>()
|
||||
|
||||
val isCompilingAgainstJdk8OrLater = state.jvmBackendClassResolver.resolveToClassDescriptors(
|
||||
@@ -157,7 +160,7 @@ class JvmBackendContext(
|
||||
|
||||
override val internalPackageFqn = FqName("kotlin.jvm")
|
||||
|
||||
val suspendLambdaToOriginalFunctionMap = ConcurrentHashMap<IrFunctionReference, IrFunction>()
|
||||
val suspendLambdaToOriginalFunctionMap = ConcurrentHashMap<IrAttributeContainer, IrFunction>()
|
||||
val suspendFunctionOriginalToView = ConcurrentHashMap<IrSimpleFunction, IrSimpleFunction>()
|
||||
|
||||
val staticDefaultStubs = ConcurrentHashMap<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
||||
|
||||
+29
-3
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlock
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
@@ -75,9 +76,11 @@ private fun IrFunction.isInlineArrayConstructor(context: JvmBackendContext): Boo
|
||||
it == context.irBuiltIns.arrayClass || it in context.irBuiltIns.primitiveArraysToPrimitiveTypes
|
||||
}
|
||||
|
||||
fun IrFunction.isInlineOnly(): Boolean =
|
||||
(isInline && hasAnnotation(INLINE_ONLY_ANNOTATION_FQ_NAME)) ||
|
||||
(this is IrSimpleFunction && correspondingPropertySymbol?.owner?.hasAnnotation(INLINE_ONLY_ANNOTATION_FQ_NAME) == true)
|
||||
fun IrDeclaration.isInlineOnly(): Boolean =
|
||||
this is IrFunction && (
|
||||
(isInline && hasAnnotation(INLINE_ONLY_ANNOTATION_FQ_NAME)) ||
|
||||
(this is IrSimpleFunction && correspondingPropertySymbol?.owner?.hasAnnotation(INLINE_ONLY_ANNOTATION_FQ_NAME) == true)
|
||||
)
|
||||
|
||||
fun IrDeclarationWithVisibility.isEffectivelyInlineOnly(): Boolean =
|
||||
this is IrFunction && (isReifiable() || isInlineOnly() || isPrivateInlineSuspend())
|
||||
@@ -87,3 +90,26 @@ fun IrFunction.isPrivateInlineSuspend(): Boolean =
|
||||
|
||||
fun IrFunction.isReifiable(): Boolean =
|
||||
typeParameters.any { it.isReified }
|
||||
|
||||
private fun IrAttributeContainer.getDeclarationBeforeInline(): IrDeclaration? {
|
||||
val original = this.attributeOwnerIdBeforeInline ?: return null
|
||||
return when (original) {
|
||||
is IrClass -> return original
|
||||
is IrFunctionExpression -> original.function
|
||||
is IrFunctionReference -> original.symbol.owner
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun IrAttributeContainer.getAttributeOwnerBeforeInline(): IrAttributeContainer? {
|
||||
if (this.attributeOwnerIdBeforeInline == null) return null
|
||||
return generateSequence(this) { it.attributeOwnerIdBeforeInline }.last()
|
||||
}
|
||||
|
||||
val IrDeclaration.fileParentBeforeInline: IrFile
|
||||
get() {
|
||||
val original = (this as? IrAttributeContainer)?.getDeclarationBeforeInline()
|
||||
?: this.parentClassOrNull?.getDeclarationBeforeInline()
|
||||
?: this
|
||||
return original.fileParent
|
||||
}
|
||||
@@ -205,7 +205,7 @@ fun IrExpression.implicitCastIfNeededTo(type: IrType) =
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.IMPLICIT_CAST, type, this)
|
||||
|
||||
fun IrFunctionAccessExpression.usesDefaultArguments(): Boolean =
|
||||
symbol.owner.valueParameters.any { this.getValueArgument(it.index) == null }
|
||||
symbol.owner.valueParameters.any { this.getValueArgument(it.index) == null && (!it.isVararg || it.defaultValue != null) }
|
||||
|
||||
fun IrValueParameter.createStubDefaultValue(): IrExpressionBody =
|
||||
factory.createExpressionBody(
|
||||
|
||||
Reference in New Issue
Block a user