Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2010-2019 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.backend.common.lower
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedString
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
// TODO: fix expect/actual default parameters
open class DefaultArgumentStubGenerator(
open val context: CommonBackendContext,
private val skipInlineMethods: Boolean = true,
private val skipExternalMethods: Boolean = false,
private val forceSetOverrideSymbols: Boolean = true
) : DeclarationTransformer {
override val withLocalDeclarations: Boolean get() = true
override fun transformFlat(declaration: IrDeclaration): List? {
if (declaration is IrFunction) {
return lower(declaration)
}
return null
}
private fun lower(irFunction: IrFunction): List? {
val newIrFunction =
irFunction.generateDefaultsFunction(
context,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(irFunction),
useConstructorMarker(irFunction)
) ?: return null
if (newIrFunction.isFakeOverride) {
return listOf(irFunction, newIrFunction)
}
log { "$irFunction -> $newIrFunction" }
val builder = context.createIrBuilder(newIrFunction.symbol)
newIrFunction.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += builder.irBlockBody(newIrFunction) {
val params = mutableListOf()
val variables = mutableMapOf()
irFunction.dispatchReceiverParameter?.let {
variables[it] = newIrFunction.dispatchReceiverParameter!!
}
irFunction.extensionReceiverParameter?.let {
variables[it] = newIrFunction.extensionReceiverParameter!!
}
// In order to deal with forward references in default value lambdas,
// accesses to the parameter before it has been determined if there is
// a default value or not is redirected to the actual parameter of the
// $default function. This is to ensure that examples such as:
//
// fun f(f1: () -> String = { f2() },
// f2: () -> String = { "OK" }) = f1()
//
// works correctly so that `f() { "OK" }` returns "OK" and
// `f()` throws a NullPointerException.
irFunction.valueParameters.associateWithTo(variables) {
newIrFunction.valueParameters[it.index]
}
generateSuperCallHandlerCheckIfNeeded(irFunction, newIrFunction)
val intAnd = this@DefaultArgumentStubGenerator.context.ir.symbols.getBinaryOperator(
OperatorNameConventions.AND, context.irBuiltIns.intType, context.irBuiltIns.intType
)
var sourceParameterIndex = -1
for (valueParameter in irFunction.valueParameters) {
if (!valueParameter.isMovedReceiver()) {
++sourceParameterIndex
}
val parameter = newIrFunction.valueParameters[valueParameter.index]
val remapped = valueParameter.defaultValue?.let { defaultValue ->
val mask = irGet(newIrFunction.valueParameters[irFunction.valueParameters.size + valueParameter.index / 32])
val bit = irInt(1 shl (sourceParameterIndex % 32))
val defaultFlag =
irCallOp(intAnd, context.irBuiltIns.intType, mask, bit)
val expression = defaultValue.expression
.prepareToBeUsedIn(newIrFunction)
.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
log { "GetValue: ${expression.symbol.owner}" }
val valueSymbol = variables[expression.symbol.owner] ?: return expression
return irGet(valueSymbol)
}
}, null)
selectArgumentOrDefault(defaultFlag, parameter, expression)
} ?: parameter
params.add(remapped)
variables[valueParameter] = remapped
}
when (irFunction) {
is IrConstructor -> +irDelegatingConstructorCall(irFunction).apply {
passTypeArgumentsFrom(newIrFunction.parentAsClass)
// This is for Kotlin/Native, which differs from the other backends in that constructors
// apparently do have dispatch receivers (though *probably* not type arguments, but copy
// those as well just in case):
passTypeArgumentsFrom(newIrFunction, offset = newIrFunction.parentAsClass.typeParameters.size)
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
}
is IrSimpleFunction -> +irReturn(dispatchToImplementation(irFunction, newIrFunction, params))
else -> error("Unknown function declaration")
}
}.statements
}
return listOf(irFunction, newIrFunction)
}
/**
* Prepares the default value to be used inside the `function` body by patching the parents.
* In K/JS it also copies the expression in order to avoid duplicate declarations after this lowering.
*
* In K/JVM copying doesn't preserve metadata, so the following case won't work:
*
* ```
* import kotlin.reflect.jvm.reflect
*
* fun foo(x: Function<*> = {}) {
* // Will print "null" if lambda is copied
* println(x.reflect())
* }
* ```
*
* Thus the duplicate declarations during the lowering pipeline is considered to be a lesser evil.
*/
protected open fun IrExpression.prepareToBeUsedIn(function: IrFunction): IrExpression {
return patchDeclarationParents(function)
}
protected open fun IrBlockBodyBuilder.selectArgumentOrDefault(
defaultFlag: IrExpression,
parameter: IrValueParameter,
default: IrExpression
): IrValueDeclaration {
// For the JVM backend, we have to generate precisely this code because that results in the
// bytecode the inliner expects see `expandMaskConditionsAndUpdateVariableNodes`. In short,
// the bytecode sequence should be
//
// -- no loads of the parameter here, as after inlining its value will be uninitialized
// ILOAD
// ICONST
// IAND
// IFEQ Lx
// -- any code inserted here is removed if the call site specifies the parameter
// STORE
// -- no jumps here
// Lx
//
// This control flow limits us to an if-then (without an else), and this together with the
// restriction on loading the parameter in the default case means we cannot create any temporaries.
+irIfThen(irNotEquals(defaultFlag, irInt(0)), irSet(parameter.symbol, default))
return parameter
}
protected open fun getOriginForCallToImplementation(): IrStatementOrigin? = null
private fun IrBlockBodyBuilder.dispatchToImplementation(
irFunction: IrSimpleFunction,
newIrFunction: IrFunction,
params: MutableList
): IrExpression {
val dispatchCall = irCall(irFunction, origin = getOriginForCallToImplementation()).apply {
passTypeArgumentsFrom(newIrFunction)
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
extensionReceiver = newIrFunction.extensionReceiverParameter?.let { irGet(it) }
for ((i, variable) in params.withIndex()) {
val paramType = irFunction.valueParameters[i].type
// The JVM backend doesn't introduce new variables, and hence may have incompatible types here.
val value = if (!paramType.isNullable() && variable.type.isNullable()) {
irImplicitCast(irGet(variable), paramType)
} else {
irGet(variable)
}
putValueArgument(i, value)
}
}
return if (needSpecialDispatch(irFunction)) {
val handlerDeclaration = newIrFunction.valueParameters.last()
// if $handler != null $handler(a, b, c) else foo(a, b, c)
irIfThenElse(
irFunction.returnType,
irEqualsNull(irGet(handlerDeclaration)),
dispatchCall,
generateHandleCall(handlerDeclaration, irFunction, newIrFunction, params)
)
} else dispatchCall
}
protected open fun IrBlockBodyBuilder.generateSuperCallHandlerCheckIfNeeded(
irFunction: IrFunction,
newIrFunction: IrFunction
) {
//NO-OP Stub
}
protected open fun needSpecialDispatch(irFunction: IrSimpleFunction) = false
protected open fun IrBlockBodyBuilder.generateHandleCall(
handlerDeclaration: IrValueParameter,
oldIrFunction: IrFunction,
newIrFunction: IrFunction,
params: MutableList
): IrExpression {
assert(needSpecialDispatch(oldIrFunction as IrSimpleFunction))
error("This method should be overridden")
}
protected open fun defaultArgumentStubVisibility(function: IrFunction) = DescriptorVisibilities.PUBLIC
protected open fun useConstructorMarker(function: IrFunction) = function is IrConstructor
private fun log(msg: () -> String) = context.log { "DEFAULT-REPLACER: ${msg()}" }
}
private fun IrFunction.findBaseFunctionWithDefaultArguments(skipInlineMethods: Boolean, skipExternalMethods: Boolean): IrFunction? {
val visited = mutableSetOf()
fun IrFunction.dfsImpl(): IrFunction? {
visited += this
if (isInline && skipInlineMethods) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
if (this is IrSimpleFunction) {
overriddenSymbols.forEach { overridden ->
val base = overridden.owner
if (base !in visited) base.dfsImpl()?.let { return it }
}
}
if (valueParameters.any { it.defaultValue != null }) return this
return null
}
return dfsImpl()
}
val DEFAULT_DISPATCH_CALL = object : IrStatementOriginImpl("DEFAULT_DISPATCH_CALL") {}
open class DefaultParameterInjector(
open val context: CommonBackendContext,
private val skipInline: Boolean = true,
private val skipExternalMethods: Boolean = false,
private val forceSetOverrideSymbols: Boolean = true
) : IrElementTransformerVoid(), BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(this)
}
private fun visitFunctionAccessExpression(expression: T, builder: (IrFunctionSymbol) -> T): T {
val argumentsCount = (0 until expression.valueArgumentsCount).count { expression.getValueArgument(it) != null }
if (argumentsCount == expression.symbol.owner.valueParameters.size)
return expression
val (symbol, params) = parametersForCall(expression) ?: return expression
for (i in 0 until expression.typeArgumentsCount) {
log { "$symbol[$i]: ${expression.getTypeArgument(i)}" }
}
symbol.owner.typeParameters.forEach { log { "${symbol.owner}[${it.index}] : $it" } }
return builder(symbol).apply {
copyTypeArgumentsFrom(expression)
params.forEachIndexed { i, value ->
log { "call::params@$i/${symbol.owner.valueParameters[i].name}: ${ir2string(value)}" }
putValueArgument(i, value)
}
dispatchReceiver = expression.dispatchReceiver
extensionReceiver = expression.extensionReceiver
log { "call::extension@: ${ir2string(expression.extensionReceiver)}" }
log { "call::dispatch@: ${ir2string(expression.dispatchReceiver)}" }
}
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid()
return visitFunctionAccessExpression(expression) {
with(expression) {
IrDelegatingConstructorCallImpl(
startOffset, endOffset, type, it as IrConstructorSymbol,
typeArgumentsCount = typeArgumentsCount,
valueArgumentsCount = it.owner.valueParameters.size
)
}
}
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
expression.transformChildrenVoid()
return visitFunctionAccessExpression(expression) {
with(expression) {
IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, it as IrConstructorSymbol, DEFAULT_DISPATCH_CALL)
}
}
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
expression.transformChildrenVoid()
return visitFunctionAccessExpression(expression) {
with(expression) {
IrEnumConstructorCallImpl(
startOffset, endOffset, type, it as IrConstructorSymbol,
typeArgumentsCount = typeArgumentsCount,
valueArgumentsCount = it.owner.valueParameters.size
)
}
}
}
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid()
return visitFunctionAccessExpression(expression) {
with(expression) {
IrCallImpl(
startOffset, endOffset, type, it as IrSimpleFunctionSymbol,
typeArgumentsCount = typeArgumentsCount,
valueArgumentsCount = it.owner.valueParameters.size,
origin = DEFAULT_DISPATCH_CALL,
superQualifierSymbol = superQualifierSymbol
)
}
}
}
private fun parametersForCall(expression: IrFunctionAccessExpression): Pair>? {
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val declaration = expression.symbol.owner
// We *have* to find the actual function here since on the JVM, a default stub for a function implemented
// in an interface does not leave an abstract method after being moved to DefaultImpls (see InterfaceLowering).
// Calling the fake override on an implementation of that interface would then result in a call to a method
// that does not actually exist as DefaultImpls is not part of the inheritance hierarchy.
val stubFunction = declaration.findBaseFunctionWithDefaultArguments(skipInline, skipExternalMethods)
?.generateDefaultsFunction(
context,
skipInline,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(declaration),
useConstructorMarker(declaration)
) ?: return null
log { "$declaration -> $stubFunction" }
val realArgumentsNumber = declaration.valueParameters.size
val maskValues = IntArray((declaration.valueParameters.size + 31) / 32)
assert((stubFunction.valueParameters.size - realArgumentsNumber - maskValues.size) in listOf(0, 1)) {
"argument count mismatch: expected $realArgumentsNumber arguments + ${maskValues.size} masks + optional handler/marker, " +
"got ${stubFunction.valueParameters.size} total in ${stubFunction.render()}"
}
var sourceParameterIndex = -1
return stubFunction.symbol to stubFunction.valueParameters.mapIndexed { i, parameter ->
if (!parameter.isMovedReceiver()) {
++sourceParameterIndex
}
when {
i >= realArgumentsNumber + maskValues.size -> IrConstImpl.constNull(startOffset, endOffset, parameter.type)
i >= realArgumentsNumber -> IrConstImpl.int(startOffset, endOffset, parameter.type, maskValues[i - realArgumentsNumber])
else -> {
val valueArgument = expression.getValueArgument(i)
if (valueArgument == null) {
maskValues[i / 32] = maskValues[i / 32] or (1 shl (sourceParameterIndex % 32))
}
valueArgument ?: nullConst(startOffset, endOffset, parameter)?.let {
IrCompositeImpl(
expression.startOffset,
expression.endOffset,
parameter.type,
IrStatementOrigin.DEFAULT_VALUE,
listOf(it)
)
}
}
}
}
}
protected open fun nullConst(startOffset: Int, endOffset: Int, irParameter: IrValueParameter): IrExpression? =
if (irParameter.varargElementType != null) {
null
} else {
nullConst(startOffset, endOffset, irParameter.type)
}
protected open fun nullConst(startOffset: Int, endOffset: Int, type: IrType): IrExpression =
IrConstImpl.defaultValueForType(startOffset, endOffset, type)
protected open fun defaultArgumentStubVisibility(function: IrFunction) = DescriptorVisibilities.PUBLIC
protected open fun useConstructorMarker(function: IrFunction) = function is IrConstructor
private fun log(msg: () -> String) = context.log { "DEFAULT-INJECTOR: ${msg()}" }
}
// Remove default argument initializers.
class DefaultParameterCleaner(
val context: CommonBackendContext,
val replaceDefaultValuesWithStubs: Boolean = false
) : DeclarationTransformer {
override val withLocalDeclarations: Boolean get() = true
override fun transformFlat(declaration: IrDeclaration): List? {
if (declaration is IrValueParameter && declaration.defaultValue != null) {
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")
)
}
} else {
declaration.defaultValue = null
}
}
return null
}
}
// Sets overriden symbols. Should be used in case `forceSetOverrideSymbols = false`
class DefaultParameterPatchOverridenSymbolsLowering(
val context: CommonBackendContext
) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List? {
if (declaration is IrSimpleFunction) {
(context.mapping.defaultArgumentsOriginalFunction[declaration] as? IrSimpleFunction)?.run {
declaration.overriddenSymbols += overriddenSymbols.mapNotNull {
(context.mapping.defaultArgumentsDispatchFunction[it.owner] as? IrSimpleFunction)?.symbol
}
}
}
return null
}
}
private fun IrFunction.generateDefaultsFunction(
context: CommonBackendContext,
skipInlineMethods: Boolean,
skipExternalMethods: Boolean,
forceSetOverrideSymbols: Boolean,
visibility: DescriptorVisibility,
useConstructorMarker: Boolean
): IrFunction? {
if (skipInlineMethods && isInline) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
if (context.mapping.defaultArgumentsOriginalFunction[this] != null) return null
context.mapping.defaultArgumentsDispatchFunction[this]?.let { return it }
if (this is IrSimpleFunction) {
// If this is an override of a function with default arguments, produce a fake override of a default stub.
if (overriddenSymbols.any { it.owner.findBaseFunctionWithDefaultArguments(skipInlineMethods, skipExternalMethods) != null })
return generateDefaultsFunctionImpl(
context, IrDeclarationOrigin.FAKE_OVERRIDE, visibility, true, useConstructorMarker
).also { defaultsFunction ->
context.mapping.defaultArgumentsDispatchFunction[this] = defaultsFunction
context.mapping.defaultArgumentsOriginalFunction[defaultsFunction] = this
if (forceSetOverrideSymbols) {
(defaultsFunction as IrSimpleFunction).overriddenSymbols += overriddenSymbols.mapNotNull {
it.owner.generateDefaultsFunction(
context,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
visibility,
useConstructorMarker
)?.symbol as IrSimpleFunctionSymbol?
}
}
}
}
// Note: this is intentionally done *after* checking for overrides. While normally `override fun`s
// have no default parameters, there is an exception in case of interface delegation:
// interface I {
// fun f(x: Int = 1)
// }
// class C(val y: I) : I by y {
// // implicit `override fun f(x: Int) = y.f(x)` has a default value for `x`
// }
// Since this bug causes the metadata serializer to write the "has default value" flag into compiled
// binaries, it's way too late to fix it. Hence the workaround.
if (valueParameters.any { it.defaultValue != null }) {
return generateDefaultsFunctionImpl(
context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, visibility, false, useConstructorMarker
).also {
context.mapping.defaultArgumentsDispatchFunction[this] = it
context.mapping.defaultArgumentsOriginalFunction[it] = this
}
}
return null
}
private fun IrFunction.generateDefaultsFunctionImpl(
context: CommonBackendContext,
newOrigin: IrDeclarationOrigin,
newVisibility: DescriptorVisibility,
isFakeOverride: Boolean,
useConstructorMarker: Boolean
): IrFunction {
val newFunction = when (this) {
is IrConstructor ->
factory.buildConstructor {
updateFrom(this@generateDefaultsFunctionImpl)
origin = newOrigin
isExternal = false
isPrimary = false
isExpect = false
visibility = newVisibility
}
is IrSimpleFunction ->
factory.buildFun {
updateFrom(this@generateDefaultsFunctionImpl)
name = Name.identifier("${[email protected]}\$default")
origin = newOrigin
this.isFakeOverride = isFakeOverride
modality = Modality.FINAL
isExternal = false
isTailrec = false
visibility = newVisibility
}
else -> throw IllegalStateException("Unknown function type")
}
(newFunction as? IrAttributeContainer)?.copyAttributes(this@generateDefaultsFunctionImpl as? IrAttributeContainer)
newFunction.copyTypeParametersFrom(this)
newFunction.parent = parent
newFunction.returnType = returnType.remapTypeParameters(classIfConstructor, newFunction.classIfConstructor)
newFunction.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(newFunction)
newFunction.extensionReceiverParameter = extensionReceiverParameter?.copyTo(newFunction)
newFunction.valueParameters = valueParameters.map {
val newType = it.type.remapTypeParameters(classIfConstructor, newFunction.classIfConstructor)
val makeNullable = it.defaultValue != null &&
(context.ir.unfoldInlineClassType(it.type) ?: it.type) !in context.irBuiltIns.primitiveIrTypes
it.copyTo(
newFunction,
type = if (makeNullable) newType.makeNullable() else newType,
defaultValue = if (it.defaultValue != null) {
factory.createExpressionBody(IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.type, "Default Stub"))
} else null,
isAssignable = it.defaultValue != null
)
}
for (i in 0 until (valueParameters.size + 31) / 32) {
newFunction.addValueParameter("mask$i".synthesizedString, context.irBuiltIns.intType, IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION)
}
if (useConstructorMarker) {
val markerType = context.ir.symbols.defaultConstructorMarker.defaultType.makeNullable()
newFunction.addValueParameter("marker".synthesizedString, markerType, IrDeclarationOrigin.DEFAULT_CONSTRUCTOR_MARKER)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
newFunction.addValueParameter(
"handler".synthesizedString,
context.irBuiltIns.anyNType,
IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION
)
}
// TODO some annotations are needed (e.g. @JvmStatic), others need different values (e.g. @JvmName), the rest are redundant.
newFunction.copyAnnotationsFrom(this)
return newFunction
}
private fun IrValueParameter.isMovedReceiver() =
origin == IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER || origin == IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER