JVM_IR: use DefaultParameterInjector

This commit is contained in:
Georgy Bronnikov
2019-07-05 19:21:38 +03:00
parent 52c00ba2a1
commit 924fa6f9d3
9 changed files with 395 additions and 200 deletions
@@ -975,6 +975,13 @@ open class WrappedFieldDescriptor(
override fun <V : Any?> getUserData(key: CallableDescriptor.UserDataKey<V>?): V? = null override fun <V : Any?> getUserData(key: CallableDescriptor.UserDataKey<V>?): V? = null
} }
fun wrappedSimpleFunctionDescriptorBasedOn(descriptor: SimpleFunctionDescriptor) =
if (descriptor is DescriptorWithContainerSource)
// TODO: Do we ever need annotations and source for these?
WrappedFunctionDescriptorWithContainerSource(descriptor.containerSource)
else
WrappedSimpleFunctionDescriptor(descriptor.annotations, descriptor.source)
private fun getContainingDeclaration(declaration: IrDeclarationWithName): DeclarationDescriptor { private fun getContainingDeclaration(declaration: IrDeclarationWithName): DeclarationDescriptor {
val parent = declaration.parent val parent = declaration.parent
return if (parent is IrClass && parent.origin == IrDeclarationOrigin.FILE_CLASS && parent.parent is IrExternalPackageFragment) { return if (parent is IrClass && parent.origin == IrDeclarationOrigin.FILE_CLASS && parent.parent is IrExternalPackageFragment) {
@@ -549,15 +549,23 @@ fun createStaticFunctionWithReceivers(
oldFunction.valueParameters.map { it.copyTo(this, index = it.index + offset) } oldFunction.valueParameters.map { it.copyTo(this, index = it.index + offset) }
) )
val mapping: Map<IrValueParameter, IrValueParameter> =
(listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters)
.zip(valueParameters).toMap()
if (copyBody) { if (copyBody) {
body = oldFunction.body copyBodyToStatic(oldFunction, this)
?.transform(VariableRemapper(mapping), null)
?.patchDeclarationParents(this)
} }
metadata = oldFunction.metadata metadata = oldFunction.metadata
} }
} }
fun copyBodyToStatic(oldFunction: IrFunction, staticFunction: IrFunction) {
val mapping: Map<IrValueParameter, IrValueParameter> =
(listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters)
.zip(staticFunction.valueParameters).toMap()
staticFunction.body = oldFunction.body
?.transform(VariableRemapper(mapping), null)
?.patchDeclarationParents(staticFunction)
}
fun IrClass.underlyingType() = if (isInline)
constructors.single { it.isPrimary }.valueParameters[0].type
else defaultType
@@ -5,19 +5,11 @@
package org.jetbrains.kotlin.backend.common.lower package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass import org.jetbrains.kotlin.backend.common.descriptors.*
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
@@ -25,8 +17,10 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
@@ -37,6 +31,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
// TODO: fix expect/actual default parameters // TODO: fix expect/actual default parameters
@@ -206,173 +201,202 @@ private fun markerParameterDeclaration(function: IrFunction) =
val DEFAULT_DISPATCH_CALL = object : IrStatementOriginImpl("DEFAULT_DISPATCH_CALL") {} val DEFAULT_DISPATCH_CALL = object : IrStatementOriginImpl("DEFAULT_DISPATCH_CALL") {}
open class DefaultParameterInjector( open class DefaultParameterInjector(
val context: CommonBackendContext, open val context: CommonBackendContext,
private val skipInline: Boolean = true, private val skipInline: Boolean = true,
private val skipExternalMethods: Boolean = false private val skipExternalMethods: Boolean = false,
) : FileLoweringPass { private val shiftMaskForExtraArgs: Boolean = false
) : IrElementTransformerVoid(), BodyLoweringPass, FileLoweringPass {
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() { irFile.transformChildrenVoid(this)
private fun visitFunctionAccessExpression(
expression: IrFunctionAccessExpression,
builder: (IrFunctionSymbol) -> IrFunctionAccessExpression
): IrExpression {
val functionDeclaration = expression.symbol.owner
if (!functionDeclaration.needsDefaultArgumentsLowering(skipInline, skipExternalMethods))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == functionDeclaration.valueParameters.size)
return expression
val (symbol, params) = parametersForCall(expression)
val descriptor = symbol.descriptor
val declaration = symbol.owner
for (i in 0 until expression.typeArgumentsCount) {
log { "$descriptor [$i]: $expression.getTypeArgument(i)" }
}
declaration.typeParameters.forEach { log { "$declaration[${it.index}] : $it" } }
return builder(symbol).apply {
this.copyTypeArgumentsFrom(expression)
params.forEach {
log { "call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}" }
putValueArgument(it.first.index, it.second)
}
dispatchReceiver = expression.dispatchReceiver
extensionReceiver = expression.extensionReceiver
log { "call::extension@: ${ir2string(expression.extensionReceiver)}" }
log { "call::dispatch@: ${ir2string(expression.dispatchReceiver)}" }
}
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
super.visitDelegatingConstructorCall(expression)
return visitFunctionAccessExpression(expression) {
IrDelegatingConstructorCallImpl(
startOffset = expression.startOffset,
endOffset = expression.endOffset,
type = context.irBuiltIns.unitType,
symbol = it as IrConstructorSymbol,
descriptor = it.descriptor,
typeArgumentsCount = expression.typeArgumentsCount
)
}
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
super.visitConstructorCall(expression)
return visitFunctionAccessExpression(expression) {
IrConstructorCallImpl.fromSymbolOwner(
expression.startOffset,
expression.endOffset,
it.owner.returnType,
it as IrConstructorSymbol,
DEFAULT_DISPATCH_CALL
)
}
}
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
return visitFunctionAccessExpression(expression) {
IrCallImpl(
startOffset = expression.startOffset,
endOffset = expression.endOffset,
type = it.owner.returnType,
symbol = it,
descriptor = it.descriptor,
typeArgumentsCount = expression.typeArgumentsCount,
origin = DEFAULT_DISPATCH_CALL,
superQualifierSymbol = expression.superQualifierSymbol
)
}
}
private fun IrFunction.findSuperMethodWithDefaultArguments(): IrFunction? {
if (!needsDefaultArgumentsLowering(skipInline, skipExternalMethods)) return null
if (this !is IrSimpleFunction) return this
for (s in overriddenSymbols) {
s.owner.findSuperMethodWithDefaultArguments()?.let { return it }
}
return this
}
private fun parametersForCall(expression: IrFunctionAccessExpression): Pair<IrFunctionSymbol, List<Pair<IrValueParameter, IrExpression?>>> {
val declaration = expression.symbol.owner
val keyFunction = declaration.findSuperMethodWithDefaultArguments()!!
val realFunction =
keyFunction.generateDefaultsFunction(context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, skipInline, skipExternalMethods)
log { "$declaration -> $realFunction" }
val maskValues = Array((declaration.valueParameters.size + 31) / 32) { 0 }
val params = mutableListOf<Pair<IrValueParameter, IrExpression?>>()
params += declaration.valueParameters.mapIndexed { i, _ ->
val valueArgument = expression.getValueArgument(i)
if (valueArgument == null) {
val maskIndex = i / 32
maskValues[maskIndex] = maskValues[maskIndex] or (1 shl (i % 32))
}
val valueParameterDeclaration = realFunction.valueParameters[i]
val defaultValueArgument = if (valueParameterDeclaration.varargElementType != null) {
null
} else {
nullConst(expression, realFunction.valueParameters[i].type)
}
valueParameterDeclaration to (valueArgument ?: defaultValueArgument)
}
val startOffset = expression.startOffset
val endOffset = expression.endOffset
maskValues.forEachIndexed { i, maskValue ->
params += maskParameterDeclaration(realFunction, i) to IrConstImpl.int(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.intType,
value = maskValue
)
}
if (expression.symbol is IrConstructorSymbol) {
val defaultArgumentMarker = context.ir.symbols.defaultConstructorMarker
params += markerParameterDeclaration(realFunction) to IrGetObjectValueImpl(
startOffset = startOffset,
endOffset = endOffset,
type = defaultArgumentMarker.owner.defaultType,
symbol = defaultArgumentMarker
)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
params += realFunction.valueParameters.last() to
IrConstImpl.constNull(startOffset, endOffset, context.irBuiltIns.nothingNType)
}
params.forEach {
log { "descriptor::${realFunction.name.asString()}#${it.first.index}: ${it.first.name.asString()}" }
}
return Pair(realFunction.symbol, params)
}
private fun argumentCount(expression: IrMemberAccessExpression): Int {
var result = 0
for (i in 0 until expression.valueArgumentsCount) {
expression.getValueArgument(i)?.run { ++result }
}
return result
}
})
} }
protected open fun nullConst(expression: IrElement, type: IrType) = when { override fun lower(irBody: IrBody) {
irBody.transformChildrenVoid(this)
}
private fun visitFunctionAccessExpression(
expression: IrFunctionAccessExpression,
builder: (IrFunctionSymbol) -> IrFunctionAccessExpression
): IrExpression {
val functionDeclaration = expression.symbol.owner
if (!functionDeclaration.needsDefaultArgumentsLowering(skipInline, skipExternalMethods))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == functionDeclaration.valueParameters.size)
return expression
val (symbol, params) = parametersForCall(expression, shiftMaskForExtraArgs)
val descriptor = symbol.descriptor
val declaration = symbol.owner
for (i in 0 until expression.typeArgumentsCount) {
log { "$descriptor [$i]: $expression.getTypeArgument(i)" }
}
declaration.typeParameters.forEach { log { "$declaration[${it.index}] : $it" } }
return builder(symbol).apply {
this.copyTypeArgumentsFrom(expression)
params.forEach {
log { "call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}" }
putValueArgument(it.first.index, it.second)
}
dispatchReceiver = expression.dispatchReceiver
extensionReceiver = expression.extensionReceiver
log { "call::extension@: ${ir2string(expression.extensionReceiver)}" }
log { "call::dispatch@: ${ir2string(expression.dispatchReceiver)}" }
}
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
super.visitDelegatingConstructorCall(expression)
return visitFunctionAccessExpression(expression) {
IrDelegatingConstructorCallImpl(
startOffset = expression.startOffset,
endOffset = expression.endOffset,
type = context.irBuiltIns.unitType,
symbol = it as IrConstructorSymbol,
descriptor = it.descriptor,
typeArgumentsCount = expression.typeArgumentsCount
)
}
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
super.visitConstructorCall(expression)
return visitFunctionAccessExpression(expression) {
IrConstructorCallImpl.fromSymbolOwner(
expression.startOffset,
expression.endOffset,
expression.type,
it as IrConstructorSymbol,
DEFAULT_DISPATCH_CALL
)
}
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
super.visitEnumConstructorCall(expression)
return visitFunctionAccessExpression(expression) {
IrEnumConstructorCallImpl(
expression.startOffset,
expression.endOffset,
expression.type,
it as IrConstructorSymbol
)
}
}
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
return visitFunctionAccessExpression(expression) {
IrCallImpl(
startOffset = expression.startOffset,
endOffset = expression.endOffset,
type = expression.type,
symbol = it,
descriptor = it.descriptor,
typeArgumentsCount = expression.typeArgumentsCount,
origin = DEFAULT_DISPATCH_CALL,
superQualifierSymbol = expression.superQualifierSymbol
)
}
}
private fun IrFunction.findSuperMethodWithDefaultArguments(): IrFunction? {
if (!needsDefaultArgumentsLowering(skipInline, skipExternalMethods)) return null
if (this !is IrSimpleFunction) return this
for (s in overriddenSymbols) {
s.owner.findSuperMethodWithDefaultArguments()?.let { return it }
}
return this
}
private fun parametersForCall(
expression: IrFunctionAccessExpression,
shiftMaskForExtraArgs: Boolean
): Pair<IrFunctionSymbol, List<Pair<IrValueParameter, IrExpression?>>> {
val declaration = expression.symbol.owner
val extraArgsShift = when {
shiftMaskForExtraArgs && declaration is IrConstructor && declaration.parentAsClass.isEnumClass -> 2
shiftMaskForExtraArgs && declaration is IrConstructor && declaration.parentAsClass.isInner -> 1 // skip the `$outer` parameter
else -> 0
}
val keyFunction = declaration.findSuperMethodWithDefaultArguments()!!
val realFunction =
keyFunction.generateDefaultsFunction(
context,
IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER,
skipInline,
skipExternalMethods
)
log { "$declaration -> $realFunction" }
val maskValues = Array((declaration.valueParameters.size - extraArgsShift + 31) / 32) { 0 }
val params = mutableListOf<Pair<IrValueParameter, IrExpression?>>()
params += declaration.valueParameters.mapIndexed { argIndex, _ ->
val valueArgument = expression.getValueArgument(argIndex)
val shiftedArgIndex = argIndex - extraArgsShift
if (valueArgument == null) {
val maskIndex = shiftedArgIndex / 32
maskValues[maskIndex] = maskValues[maskIndex] or (1 shl (shiftedArgIndex % 32))
}
val valueParameterDeclaration = realFunction.valueParameters[argIndex]
val defaultValueArgument = if (valueParameterDeclaration.varargElementType != null) {
null
} else {
nullConst(expression, realFunction.valueParameters[argIndex].type)
}
valueParameterDeclaration to (valueArgument ?: defaultValueArgument)
}
val startOffset = expression.startOffset
val endOffset = expression.endOffset
maskValues.forEachIndexed { i, maskValue ->
params += maskParameterDeclaration(realFunction, i) to IrConstImpl.int(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.intType,
value = maskValue
)
}
if (expression.symbol is IrConstructorSymbol) {
val defaultArgumentMarker = context.ir.symbols.defaultConstructorMarker
params += markerParameterDeclaration(realFunction) to
IrConstImpl.constNull(startOffset, endOffset, defaultArgumentMarker.owner.defaultType)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
params += realFunction.valueParameters.last() to
IrConstImpl.constNull(startOffset, endOffset, context.irBuiltIns.nothingNType)
}
params.forEach {
log { "descriptor::${realFunction.name.asString()}#${it.first.index}: ${it.first.name.asString()}" }
}
return Pair(realFunction.symbol, params)
}
private fun argumentCount(expression: IrMemberAccessExpression): Int {
var result = 0
for (i in 0 until expression.valueArgumentsCount) {
expression.getValueArgument(i)?.run { ++result }
}
return result
}
protected open fun nullConst(expression: IrElement, type: IrType): IrExpression = when {
type.isFloat() -> IrConstImpl.float(expression.startOffset, expression.endOffset, type, 0.0F) type.isFloat() -> IrConstImpl.float(expression.startOffset, expression.endOffset, type, 0.0F)
type.isDouble() -> IrConstImpl.double(expression.startOffset, expression.endOffset, type, 0.0) type.isDouble() -> IrConstImpl.double(expression.startOffset, expression.endOffset, type, 0.0)
type.isBoolean() -> IrConstImpl.boolean(expression.startOffset, expression.endOffset, type, false) type.isBoolean() -> IrConstImpl.boolean(expression.startOffset, expression.endOffset, type, false)
@@ -393,6 +417,16 @@ class DefaultParameterCleaner constructor(val context: CommonBackendContext) : F
} }
} }
// Some later lowerings produce bodies that need to undergo default parameter injection.
fun IrBody.insertCallsToDefaultArgumentStubs(
context: CommonBackendContext,
skipInline: Boolean = true,
skipExternalMethods: Boolean = false,
shiftMaskForExtraArgs: Boolean = false
) {
DefaultParameterInjector(context, skipInline, skipExternalMethods, shiftMaskForExtraArgs).lower(this)
}
// TODO this implementation is exponential // TODO this implementation is exponential
private fun IrFunction.needsDefaultArgumentsLowering(skipInlineMethods: Boolean, skipExternalMethods: Boolean): Boolean { private fun IrFunction.needsDefaultArgumentsLowering(skipInlineMethods: Boolean, skipExternalMethods: Boolean): Boolean {
if (isInline && skipInlineMethods) return false if (isInline && skipInlineMethods) return false
@@ -434,7 +468,7 @@ private fun IrFunction.generateDefaultsFunctionImpl(
} }
newFunction.copyTypeParametersFrom(this) newFunction.copyTypeParametersFrom(this)
val newValueParameters = valueParameters.map { it.copyTo(newFunction) } + syntheticParameters val newValueParameters = valueParameters.map { it.copyMaybeNullableTo(newFunction, context.irBuiltIns) } + syntheticParameters
newValueParameters.forEach { newValueParameters.forEach {
it.defaultValue = null it.defaultValue = null
} }
@@ -485,7 +519,7 @@ private fun buildFunctionDeclaration(irFunction: IrFunction, origin: IrDeclarati
} }
} }
is IrSimpleFunction -> { is IrSimpleFunction -> {
val descriptor = WrappedSimpleFunctionDescriptor(irFunction.descriptor.annotations, irFunction.descriptor.source) val descriptor = wrappedSimpleFunctionDescriptorBasedOn(irFunction.descriptor as SimpleFunctionDescriptor)
val name = Name.identifier("${irFunction.name}\$default") val name = Name.identifier("${irFunction.name}\$default")
return IrFunctionImpl( return IrFunctionImpl(
@@ -543,3 +577,26 @@ private fun IrFunction.valueParameter(index: Int, name: Name, type: IrType): IrV
internal val kConstructorMarkerName = "marker".synthesizedName internal val kConstructorMarkerName = "marker".synthesizedName
private fun parameterMaskName(number: Int) = "mask$number".synthesizedName private fun parameterMaskName(number: Int) = "mask$number".synthesizedName
private fun IrValueParameter.copyMaybeNullableTo(irFunction: IrFunction, irBuiltIns: IrBuiltIns): IrValueParameter {
if (defaultValue == null) return copyTo(irFunction)
val underlyingType = type.classOrNull?.owner?.underlyingType() ?: type
if (underlyingType in irBuiltIns.primitiveIrTypes) return copyTo(irFunction)
val newType = type.remapTypeParameters(
(parent as IrTypeParametersContainer).classIfConstructor,
irFunction.classIfConstructor
).makeNullable()
val descriptor = WrappedValueParameterDescriptor(symbol.descriptor.annotations, symbol.descriptor.source)
return IrValueParameterImpl(
startOffset, endOffset, origin, IrValueParameterSymbolImpl(descriptor),
name, index, newType, varargElementType, isCrossinline, isNoinline
).also {
descriptor.bind(it)
it.parent = irFunction
it.defaultValue = null
it.annotations.addAll(annotations.map { it.deepCopyWithSymbols() })
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -67,6 +68,8 @@ class JvmBackendContext(
val suspendLambdaToOriginalFunctionMap = mutableMapOf<IrClass, IrFunction>() val suspendLambdaToOriginalFunctionMap = mutableMapOf<IrClass, IrFunction>()
val continuationClassBuilders = mutableMapOf<IrClass, ClassBuilder>() val continuationClassBuilders = mutableMapOf<IrClass, ClassBuilder>()
val staticDefaultStubs = mutableMapOf<IrFunctionSymbol, IrFunction>()
internal fun getTopLevelClass(fqName: FqName): IrClassSymbol { internal fun getTopLevelClass(fqName: FqName): IrClassSymbol {
val descriptor = state.module.getPackage(fqName.parent()).memberScope.getContributedClassifier( val descriptor = state.module.getPackage(fqName.parent()).memberScope.getContributedClassifier(
fqName.shortName(), NoLookupLocation.FROM_BACKEND fqName.shortName(), NoLookupLocation.FROM_BACKEND
@@ -91,6 +91,13 @@ private val defaultArgumentStubPhase = makeIrFilePhase<CommonBackendContext>(
prerequisite = setOf(localDeclarationsPhase) prerequisite = setOf(localDeclarationsPhase)
) )
private val defaultArgumentInjectorPhase = makeIrFilePhase(
::JvmDefaultParameterInjector,
name = "DefaultParameterInjector",
description = "Transform calls with default arguments into calls to stubs",
prerequisite = setOf(defaultArgumentStubPhase)
)
private val innerClassesPhase = makeIrFilePhase( private val innerClassesPhase = makeIrFilePhase(
::InnerClassesLowering, ::InnerClassesLowering,
name = "InnerClasses", name = "InnerClasses",
@@ -127,10 +134,12 @@ private val jvmFilePhases =
callableReferencePhase then callableReferencePhase then
localDeclarationsPhase then localDeclarationsPhase then
defaultArgumentStubPhase then defaultArgumentStubPhase then
defaultArgumentInjectorPhase then
interfacePhase then interfacePhase then
interfaceDelegationPhase then interfaceDelegationPhase then
interfaceSuperCallsPhase then interfaceSuperCallsPhase then
interfaceDefaultCallsPhase then
singleAbstractMethodPhase then singleAbstractMethodPhase then
addContinuationPhase then addContinuationPhase then
@@ -145,13 +154,13 @@ private val jvmFilePhases =
enumClassPhase then enumClassPhase then
objectClassPhase then objectClassPhase then
makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then
syntheticAccessorPhase then
collectionStubMethodLowering then collectionStubMethodLowering then
bridgePhase then bridgePhase then
jvmOverloadsAnnotationPhase then jvmOverloadsAnnotationPhase then
jvmDefaultConstructorPhase then jvmDefaultConstructorPhase then
jvmStaticAnnotationPhase then jvmStaticAnnotationPhase then
staticDefaultFunctionPhase then staticDefaultFunctionPhase then
syntheticAccessorPhase then
toArrayPhase then toArrayPhase then
flattenStringConcatenationPhase then flattenStringConcatenationPhase then
@@ -211,6 +211,34 @@ private class InterfaceSuperCallsLowering(val context: JvmBackendContext) : IrEl
} }
} }
internal val interfaceDefaultCallsPhase = makeIrFilePhase(
lowering = ::InterfaceDefaultCallsLowering,
name = "InterfaceDefaultCalls",
description = "Redirect interface calls with default arguments to DefaultImpls"
)
private class InterfaceDefaultCallsLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitCall(expression: IrCall): IrExpression {
val callee = expression.symbol.owner
if (callee.parent.safeAs<IrClass>()?.isInterface != true ||
callee.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
) {
return super.visitCall(expression)
}
val redirectTarget = context.declarationFactory.getDefaultImplsFunction(callee as IrSimpleFunction)
val newCall = irCall(expression, redirectTarget, receiversAsArguments = true)
return super.visitCall(newCall)
}
}
private fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod() = private fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod() =
resolveFakeOverride()?.let { origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB } == true || resolveFakeOverride()?.let { origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB } == true ||
@@ -0,0 +1,42 @@
/*
* 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.jvm.lower
import org.jetbrains.kotlin.backend.common.ir.underlyingType
import org.jetbrains.kotlin.backend.common.lower.DefaultParameterInjector
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.types.*
class JvmDefaultParameterInjector(context: JvmBackendContext) :
DefaultParameterInjector(context, skipInline = false, skipExternalMethods = false) {
override val context: JvmBackendContext get() = super.context as JvmBackendContext
override fun nullConst(expression: IrElement, type: IrType): IrExpression {
if (type !is IrSimpleType ||
type.hasQuestionMark ||
type.classOrNull?.owner?.isInline != true
) {
return super.nullConst(expression, type)
}
val underlyingType = type.classOrNull!!.owner.underlyingType()
return IrCallImpl(
expression.startOffset, expression.endOffset,
type,
context.ir.symbols.unsafeCoerceIntrinsicSymbol, context.ir.symbols.unsafeCoerceIntrinsicSymbol.descriptor,
typeArgumentsCount = 2,
valueArgumentsCount = 1
).apply {
putTypeArgument(0, underlyingType) // from
putTypeArgument(1, type) // to
putValueArgument(0, super.nullConst(expression, underlyingType))
}
}
}
@@ -16,30 +16,45 @@
package org.jetbrains.kotlin.backend.jvm.lower package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.copyBodyToStatic
import org.jetbrains.kotlin.backend.common.ir.createStaticFunctionWithReceivers import org.jetbrains.kotlin.backend.common.ir.createStaticFunctionWithReceivers
import org.jetbrains.kotlin.backend.common.phaser.SameTypeNamedPhaseWrapper
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.phaser.then
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrReturn import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
internal val staticDefaultFunctionPhase = makeIrFilePhase( private val functionDefinitionLoweringPhase = makeIrFilePhase(
::StaticDefaultFunctionLowering, ::StaticDefaultFunctionLowering,
name = "StaticDefaultFunction", name = "StaticDefaultFunctionDefinition",
description = "Generate static functions for default parameters" description = "Generate static functions for default parameters"
) )
private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), ClassLoweringPass { private val callLoweringPhase = makeIrFilePhase(
constructor(@Suppress("UNUSED_PARAMETER") context: BackendContext) : this() ::StaticDefaultCallLowering,
name = "StaticDefaultCall",
description = "Generate calls of static functions for default parameters"
)
val updatedFunctions = hashMapOf<IrFunctionSymbol, IrFunction>() internal val staticDefaultFunctionPhase = SameTypeNamedPhaseWrapper(
name = "StaticDefaultFunction",
description = "Make function adapters for default arguments static",
lower = functionDefinitionLoweringPhase then callLoweringPhase,
prerequisite = setOf(jvmStaticAnnotationPhase),
nlevels = 1
)
private class StaticDefaultFunctionLowering(val context: JvmBackendContext) : IrElementTransformerVoid(),
ClassLoweringPass {
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
irClass.accept(this, null) irClass.accept(this, null)
@@ -47,9 +62,9 @@ private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), Clas
override fun visitFunction(declaration: IrFunction): IrStatement { override fun visitFunction(declaration: IrFunction): IrStatement {
return if (declaration.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && declaration.dispatchReceiverParameter != null) { return if (declaration.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && declaration.dispatchReceiverParameter != null) {
return createStaticFunctionWithReceivers(declaration.parent, declaration.name, declaration).also { context.getStaticFunctionWithReceivers(declaration).also {
updatedFunctions[declaration.symbol] = it copyBodyToStatic(declaration, it)
super.visitFunction(declaration) super.visitFunction(it)
} }
} else { } else {
super.visitFunction(declaration) super.visitFunction(declaration)
@@ -58,9 +73,9 @@ private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), Clas
override fun visitReturn(expression: IrReturn): IrExpression { override fun visitReturn(expression: IrReturn): IrExpression {
return super.visitReturn( return super.visitReturn(
if (updatedFunctions.containsKey(expression.returnTargetSymbol)) { if (context.staticDefaultStubs.containsKey(expression.returnTargetSymbol)) {
with(expression) { with(expression) {
val irFunction = updatedFunctions[expression.returnTargetSymbol]!! val irFunction = context.staticDefaultStubs[expression.returnTargetSymbol]!!
IrReturnImpl(startOffset, endOffset, expression.type, irFunction.symbol, expression.value) IrReturnImpl(startOffset, endOffset, expression.type, irFunction.symbol, expression.value)
} }
} else { } else {
@@ -69,3 +84,29 @@ private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), Clas
) )
} }
} }
private class StaticDefaultCallLowering(
val context: JvmBackendContext
) : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.accept(this, null)
}
override fun visitCall(expression: IrCall): IrExpression {
val callee = expression.symbol.owner
if (callee.origin !== IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || callee.dispatchReceiverParameter == null) {
return super.visitCall(expression)
}
val newCallee = context.getStaticFunctionWithReceivers(callee)
val newCall = irCall(expression, newCallee, receiversAsArguments = true)
return super.visitCall(newCall)
}
}
private fun JvmBackendContext.getStaticFunctionWithReceivers(function: IrFunction) =
staticDefaultStubs.getOrPut(function.symbol) {
createStaticFunctionWithReceivers(function.parent, function.name, function, copyBody = false)
}
@@ -41,7 +41,7 @@ internal val syntheticAccessorPhase = makeIrFilePhase(
::SyntheticAccessorLowering, ::SyntheticAccessorLowering,
name = "SyntheticAccessor", name = "SyntheticAccessor",
description = "Introduce synthetic accessors", description = "Introduce synthetic accessors",
prerequisite = setOf(objectClassPhase) prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase)
) )
private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {