JVM IR: handle JvmStatic in object as module phase

This allows to get rid of the situation where a JvmStatic function in
object can be seen in different states in different lowerings: unlowered
with a dispatch receiver parameter, declaration is lowered but calls are
not, and both declaration and calls are lowered.

Now it works like this:
1) JvmStatic functions in objects coming from dependencies are always
   loaded as lowered, without the extra dispatch receiver parameter. In
   psi2ir this is done via JVM-specific extension; in fir2ir it's done
   in place (but probably should be extracted to extension too).
2) Functions from sources are created as unlowered by both psi2ir and
   fir2ir, and are lowered in a module-wide phase at the beginning of
   JvmLower.
3) Calls to all JvmStatic functions from objects (from sources and
   dependencies) are lowered in the same phase at the beginning of
   JvmLower.

This ensures that all lowerings after the module-wide phase
`jvmStaticInObjectPhase`, which include all per-file phases, see all
JvmStatic functions in objects without the additional dispatch receiver
parameter, and calls do not have dispatch receiver either.

The only issue with this approach is that function/property reference
representation in reflection needs to have that dispatch receiver
parameter, and that is achieved via a hack in those lowerings, which
seems not too out of place anyway, given that they're handled specially
in kotlin-reflect as well.
This commit is contained in:
Alexander Udalov
2020-11-11 15:25:03 +01:00
parent 0a00cbefaf
commit 7117932623
13 changed files with 103 additions and 128 deletions
@@ -21,6 +21,10 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isNonCompanionObject
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
class Fir2IrLazySimpleFunction( class Fir2IrLazySimpleFunction(
components: Fir2IrComponents, components: Fir2IrComponents,
@@ -90,7 +94,9 @@ class Fir2IrLazySimpleFunction(
override var dispatchReceiverParameter: IrValueParameter? by lazyVar { override var dispatchReceiverParameter: IrValueParameter? by lazyVar {
val containingClass = parent as? IrClass val containingClass = parent as? IrClass
if (!fir.isStatic && containingClass != null) { if (containingClass != null && !fir.isStatic &&
!(containingClass.isNonCompanionObject && hasAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME))
) {
declarationStorage.enterScope(this) declarationStorage.enterScope(this)
declareThisReceiverParameter( declareThisReceiverParameter(
symbolTable, symbolTable,
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.codegen.* import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.backend.jvm.codegen.createFakeContinuation
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.backend.jvm.lower.BridgeLowering import org.jetbrains.kotlin.backend.jvm.lower.BridgeLowering
@@ -128,8 +127,6 @@ class JvmBackendContext(
val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>() val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>()
val fakeContinuation: IrExpression = createFakeContinuation(this) val fakeContinuation: IrExpression = createFakeContinuation(this)
val jvmStaticObjectFunctionToStaticFunctionMap = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
val staticDefaultStubs = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>() val staticDefaultStubs = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this) val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -79,6 +81,11 @@ class JvmGeneratorExtensions(private val generateFacades: Boolean = true) : Gene
override fun isPropertyWithPlatformField(descriptor: PropertyDescriptor): Boolean = override fun isPropertyWithPlatformField(descriptor: PropertyDescriptor): Boolean =
descriptor.hasJvmFieldAnnotation() descriptor.hasJvmFieldAnnotation()
override fun isStaticFunction(descriptor: FunctionDescriptor): Boolean =
DescriptorUtils.isNonCompanionObject(descriptor.containingDeclaration) &&
(descriptor.hasJvmStaticAnnotation() ||
descriptor is PropertyAccessorDescriptor && descriptor.correspondingProperty.hasJvmStaticAnnotation())
override val enhancedNullability: EnhancedNullability override val enhancedNullability: EnhancedNullability
get() = JvmEnhancedNullability get() = JvmEnhancedNullability
@@ -360,7 +360,7 @@ private val jvmFilePhases = listOf(
initializersPhase, initializersPhase,
initializersCleanupPhase, initializersCleanupPhase,
functionNVarargBridgePhase, functionNVarargBridgePhase,
jvmStaticAnnotationPhase, jvmStaticInCompanionPhase,
staticDefaultFunctionPhase, staticDefaultFunctionPhase,
bridgePhase, bridgePhase,
syntheticAccessorPhase, syntheticAccessorPhase,
@@ -392,6 +392,7 @@ val jvmPhases = NamedCompilerPhase(
expectDeclarationsRemovingPhase then expectDeclarationsRemovingPhase then
scriptsToClassesPhase then scriptsToClassesPhase then
fileClassPhase then fileClassPhase then
jvmStaticInObjectPhase then
performByIrFile(lower = jvmFilePhases) then performByIrFile(lower = jvmFilePhases) then
generateMultifileFacadesPhase then generateMultifileFacadesPhase then
resolveInlineCallsPhase then resolveInlineCallsPhase then
@@ -18,14 +18,12 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
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.*
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
@@ -111,7 +109,9 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
// However, when we bind a value of an inline class type as a receiver, the receiver will turn into an argument of // However, when we bind a value of an inline class type as a receiver, the receiver will turn into an argument of
// the function in question. Yet we still need to record it as the "receiver" in CallableReference in order for reflection // the function in question. Yet we still need to record it as the "receiver" in CallableReference in order for reflection
// to work correctly. // to work correctly.
private val boundReceiver: Pair<IrValueParameter, IrExpression>? = irFunctionReference.getArgumentsWithIr().singleOrNull() private val boundReceiver: Pair<IrValueParameter, IrExpression>? =
if (callee.isJvmStaticInObject()) createFakeBoundReceiverForJvmStaticInObject()
else irFunctionReference.getArgumentsWithIr().singleOrNull()
// The type of the reference is KFunction<in A1, ..., in An, out R> // The type of the reference is KFunction<in A1, ..., in An, out R>
private val parameterTypes = (irFunctionReference.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type } private val parameterTypes = (irFunctionReference.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
@@ -485,6 +485,19 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
) )
} }
private fun createFakeBoundReceiverForJvmStaticInObject(): Pair<IrValueParameter, IrGetObjectValueImpl> {
// JvmStatic functions in objects are special in that they are generated as static methods in the bytecode, and JVM IR lowers
// both declarations and call sites early on in jvmStaticInObjectPhase because it's easier that way in subsequent lowerings.
// However from the point of view of Kotlin language (and thus reflection), these functions still take the dispatch receiver
// parameter of the object type. So we pretend here that a JvmStatic function in object has an additional dispatch receiver
// parameter, so that the correct function reference object will be created and reflective calls will work at runtime.
val objectClass = callee.parentAsClass
return buildValueParameter(callee) {
name = Name.identifier("\$this")
type = objectClass.typeWith()
} to IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, objectClass.typeWith(), objectClass.symbol)
}
private fun createLegacyMethodOverride( private fun createLegacyMethodOverride(
superFunction: IrSimpleFunction, superFunction: IrSimpleFunction,
generator: JvmIrBuilder.() -> IrExpression generator: JvmIrBuilder.() -> IrExpression
@@ -494,7 +507,6 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
irExprBody(generator()) irExprBody(generator())
} }
} }
} }
companion object { companion object {
@@ -11,23 +11,21 @@ import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.copyCorrespondingPropertyFrom
import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irExprBody import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irGetField import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
@@ -36,35 +34,37 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
internal val jvmStaticAnnotationPhase = makeIrFilePhase( internal val jvmStaticInObjectPhase = makeIrModulePhase(
::JvmStaticAnnotationLowering, ::JvmStaticInObjectLowering,
name = "JvmStaticAnnotation", name = "JvmStaticInObject",
description = "Handle JvmStatic annotations" description = "Make JvmStatic functions in non-companion objects static and replace all call sites in the module"
) )
/* internal val jvmStaticInCompanionPhase = makeIrFilePhase(
* For @JvmStatic functions within companion objects of classes, we synthesize proxy static functions that redirect ::JvmStaticInCompanionLowering,
* to the actual implementation. name = "JvmStaticInCompanion",
* For @JvmStatic functions within static objects, we make the actual function static and modify all call sites. description = "Synthesize static proxy functions for JvmStatic functions in companion objects"
*/ )
private class JvmStaticAnnotationLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
private class JvmStaticInObjectLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
CompanionObjectJvmStaticLowering(context).runOnFilePostfix(irFile)
SingletonObjectJvmStaticLowering(context).runOnFilePostfix(irFile) SingletonObjectJvmStaticLowering(context).runOnFilePostfix(irFile)
irFile.transformChildrenVoid(MakeCallsStatic(context)) irFile.transformChildrenVoid(MakeCallsStatic(context))
} }
} }
private class CompanionObjectJvmStaticLowering(val context: JvmBackendContext) : ClassLoweringPass { private class JvmStaticInCompanionLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), ClassLoweringPass {
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
val companion = irClass.declarations.find { val companion = irClass.companionObject() ?: return
it is IrClass && it.isCompanion
} as? IrClass ?: return
companion.declarations companion.declarations
// In case of companion objects, proxy functions for '$default' methods for @JvmStatic functions with default parameters // In case of companion objects, proxy functions for '$default' methods for @JvmStatic functions with default parameters
// are not created in the host class. // are not created in the host class.
.filter { isJvmStaticFunction(it) && it.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER } .filter {
it.isJvmStaticDeclaration() &&
it.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER &&
it.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS
}
.forEach { declaration -> .forEach { declaration ->
val jvmStaticFunction = declaration as IrSimpleFunction val jvmStaticFunction = declaration as IrSimpleFunction
if (jvmStaticFunction.isExternal) { if (jvmStaticFunction.isExternal) {
@@ -129,107 +129,46 @@ private class CompanionObjectJvmStaticLowering(val context: JvmBackendContext) :
private class SingletonObjectJvmStaticLowering(val context: JvmBackendContext) : ClassLoweringPass { private class SingletonObjectJvmStaticLowering(val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
if (!irClass.isObject || irClass.isCompanion) return if (!irClass.isNonCompanionObject) return
val jvmStaticFunctionsToReplace = irClass.declarations.filter { for (function in irClass.simpleFunctions()) {
// dispatch receiver parameter is already null for synthetic property annotation methods if (function.isJvmStaticDeclaration()) {
isJvmStaticFunction(it) && it is IrSimpleFunction && it.dispatchReceiverParameter != null // dispatch receiver parameter is already null for synthetic property annotation methods
} function.dispatchReceiverParameter?.let { oldDispatchReceiverParameter ->
jvmStaticFunctionsToReplace.forEach { function -> function.dispatchReceiverParameter = null
val replacement = createReplacement(context, function as IrSimpleFunction) function.replaceThisByStaticReference(context.cachedDeclarations, irClass, oldDispatchReceiverParameter)
// Set dispatch receiver parameter for body move operation. }
replacement.dispatchReceiverParameter = function.dispatchReceiverParameter }
replacement.body = function.moveBodyTo(replacement)
replacement.replaceThisByStaticReference(context.cachedDeclarations, irClass, function.dispatchReceiverParameter!!)
// Clear dispatch receiver parameter again after body move operation.
replacement.dispatchReceiverParameter = null
irClass.declarations.remove(function)
irClass.declarations.add(replacement)
} }
} }
} }
private fun createReplacement( internal fun IrDeclaration.isJvmStaticInObject(): Boolean =
context: JvmBackendContext, isJvmStaticDeclaration() && (parent as? IrClass)?.isNonCompanionObject == true
jvmStaticFunction: IrSimpleFunction
): IrSimpleFunction =
context.jvmStaticObjectFunctionToStaticFunctionMap.getOrPut(jvmStaticFunction) {
val irClass = jvmStaticFunction.parentAsClass
val newFunction = context.irFactory.buildFun {
updateFrom(jvmStaticFunction)
name = jvmStaticFunction.name
returnType = jvmStaticFunction.returnType
}.apply {
parent = irClass
copyTypeParametersFrom(jvmStaticFunction)
copyAnnotationsFrom(jvmStaticFunction)
extensionReceiverParameter = jvmStaticFunction.extensionReceiverParameter?.copyTo(this)
valueParameters = jvmStaticFunction.valueParameters.map { it.copyTo(this) }
copyAttributes(jvmStaticFunction)
copyCorrespondingPropertyFrom(jvmStaticFunction)
metadata = jvmStaticFunction.metadata
}
context.jvmStaticObjectFunctionToStaticFunctionMap[jvmStaticFunction] = newFunction
newFunction
}
private fun IrFunction.isJvmStaticInSingleton(): Boolean {
val parentClass = parent as? IrClass ?: return false
return isJvmStaticFunction(this) && parentClass.isObject && !parentClass.isCompanion
}
private class MakeCallsStatic(val context: JvmBackendContext) : IrElementTransformerVoid() { private class MakeCallsStatic(val context: JvmBackendContext) : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression { override fun visitMemberAccess(expression: IrMemberAccessExpression<*>): IrExpression {
if (expression.symbol.owner.isJvmStaticInSingleton() && expression.dispatchReceiver != null) { val callee = expression.symbol.owner
// Imported functions do not have their receiver parameter nulled by SingletonObjectJvmStaticLowering, if (callee is IrDeclaration && callee.isJvmStaticInObject() && expression.dispatchReceiver != null) {
// so we have to do it here.
// TODO: would be better handled by lowering imported declarations.
val callee = expression.symbol.owner
val newCallee = if (!callee.isInCurrentModule()) {
callee.copyRemovingDispatchReceiver() // TODO: cache these
} else {
createReplacement(context, callee)
}
return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).irBlock(expression) { return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).irBlock(expression) {
// OldReceiver has to be evaluated for its side effects. // OldReceiver has to be evaluated for its side effects.
val oldReceiver = super.visitExpression(expression.dispatchReceiver!!) val oldReceiver = super.visitExpression(expression.dispatchReceiver!!)
// `coerceToUnit()` is private in InsertImplicitCasts, have to reproduce it here // `coerceToUnit()` is private in InsertImplicitCasts, have to reproduce it here
val oldReceiverVoid = IrTypeOperatorCallImpl( +IrTypeOperatorCallImpl(
oldReceiver.startOffset, oldReceiver.endOffset, oldReceiver.startOffset, oldReceiver.endOffset,
context.irBuiltIns.unitType, context.irBuiltIns.unitType,
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, IrTypeOperator.IMPLICIT_COERCION_TO_UNIT,
context.irBuiltIns.unitType, context.irBuiltIns.unitType,
oldReceiver oldReceiver
) )
expression.dispatchReceiver = null
+super.visitExpression(oldReceiverVoid) +super.visitMemberAccess(expression)
+super.visitCall(
irCall(expression, newFunction = newCallee).apply { dispatchReceiver = null }
)
} }
} }
return super.visitCall(expression) return super.visitMemberAccess(expression)
} }
private fun IrSimpleFunction.copyRemovingDispatchReceiver(): IrSimpleFunction =
factory.buildFun {
updateFrom(this@copyRemovingDispatchReceiver)
name = this@copyRemovingDispatchReceiver.name
returnType = this@copyRemovingDispatchReceiver.returnType
}.also {
it.parent = parent
it.copyCorrespondingPropertyFrom(this)
it.annotations += annotations
it.copyParameterDeclarationsFrom(this)
it.dispatchReceiverParameter = null
it.copyAttributes(this)
}
} }
private fun isJvmStaticFunction(declaration: IrDeclaration): Boolean = private fun IrDeclaration.isJvmStaticDeclaration(): Boolean =
declaration is IrSimpleFunction && hasAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) ||
(declaration.hasAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) || (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.hasAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) == true
declaration.correspondingPropertySymbol?.owner?.hasAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) == true) &&
declaration.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.util.filterOutAnnotations import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
@@ -45,7 +42,7 @@ internal val remapObjectFieldAccesses = makeIrFilePhase(
private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : ClassLoweringPass { private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
if (irClass.isObject && !irClass.isCompanion) { if (irClass.isNonCompanionObject) {
irClass.handle() irClass.handle()
} else { } else {
(irClass.declarations.singleOrNull { it is IrClass && it.isCompanion } as IrClass?)?.handle() (irClass.declarations.singleOrNull { it is IrClass && it.isCompanion } as IrClass?)?.handle()
@@ -31,11 +31,13 @@ import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.createType import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
@@ -301,9 +303,7 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).irBlock { return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).irBlock {
+referenceClass +referenceClass
+irCall(referenceClass.constructors.single()).apply { +irCall(referenceClass.constructors.single()).apply {
var index = 0 expression.getBoundReceiver()?.let { putValueArgument(0, it) }
expression.dispatchReceiver?.let { putValueArgument(index++, it) }
expression.extensionReceiver?.let { putValueArgument(index++, it) }
} }
} }
} }
@@ -397,8 +397,7 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem
} }
private fun addConstructor(expression: IrCallableReference<*>, referenceClass: IrClass, superClass: IrClass) { private fun addConstructor(expression: IrCallableReference<*>, referenceClass: IrClass, superClass: IrClass) {
// See propertyReferenceKindFor -- only one of them could ever be present. val hasBoundReceiver = expression.getBoundReceiver() != null
val hasBoundReceiver = expression.dispatchReceiver != null || expression.extensionReceiver != null
val numOfSuperArgs = val numOfSuperArgs =
(if (hasBoundReceiver) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0) (if (hasBoundReceiver) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0)
val superConstructor = superClass.constructors.single { it.valueParameters.size == numOfSuperArgs } val superConstructor = superClass.constructors.single { it.valueParameters.size == numOfSuperArgs }
@@ -434,4 +433,13 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem
} }
} }
} }
private fun IrCallableReference<*>.getBoundReceiver(): IrExpression? {
val callee = symbol.owner
return if (callee is IrDeclaration && callee.isJvmStaticInObject()) {
// See FunctionReferenceLowering.FunctionReferenceBuilder.createFakeBoundReceiverForJvmStaticInObject.
val objectClass = callee.parentAsClass
IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, objectClass.typeWith(), objectClass.symbol)
} else dispatchReceiver ?: extensionReceiver
}
} }
@@ -36,7 +36,7 @@ internal val staticDefaultFunctionPhase = makeIrFilePhase(
::StaticDefaultFunctionLowering, ::StaticDefaultFunctionLowering,
name = "StaticDefaultFunction", name = "StaticDefaultFunction",
description = "Make function adapters for default arguments static", description = "Make function adapters for default arguments static",
prerequisite = setOf(jvmStaticAnnotationPhase), prerequisite = setOf(jvmStaticInObjectPhase),
) )
private class StaticDefaultFunctionLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass { private class StaticDefaultFunctionLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.ir.declarations.lazy package org.jetbrains.kotlin.ir.declarations.lazy
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrBody
@@ -54,7 +54,7 @@ class IrLazyFunction(
override val initialSignatureFunction: IrFunction? by createInitialSignatureFunction() override val initialSignatureFunction: IrFunction? by createInitialSignatureFunction()
override var dispatchReceiverParameter: IrValueParameter? by createReceiverParameter(descriptor.dispatchReceiverParameter) override var dispatchReceiverParameter: IrValueParameter? by createReceiverParameter(descriptor.dispatchReceiverParameter, true)
override var extensionReceiverParameter: IrValueParameter? by createReceiverParameter(descriptor.extensionReceiverParameter) override var extensionReceiverParameter: IrValueParameter? by createReceiverParameter(descriptor.extensionReceiverParameter)
@@ -34,9 +34,13 @@ interface IrLazyFunctionBase : IrLazyDeclarationBase, IrTypeParametersContainer
} }
} }
fun createReceiverParameter(parameter: ReceiverParameterDescriptor?): ReadWriteProperty<Any?, IrValueParameter?> = fun createReceiverParameter(
parameter: ReceiverParameterDescriptor?,
functionDispatchReceiver: Boolean = false,
): ReadWriteProperty<Any?, IrValueParameter?> =
lazyVar { lazyVar {
typeTranslator.buildWithScope(this) { if (functionDispatchReceiver && stubGenerator.extensions.isStaticFunction(descriptor)) null
else typeTranslator.buildWithScope(this) {
parameter?.generateReceiverParameterStub()?.also { it.parent = this@IrLazyFunctionBase } parameter?.generateReceiverParameterStub()?.also { it.parent = this@IrLazyFunctionBase }
} }
} }
@@ -259,6 +259,7 @@ val IrClass.isInterface get() = kind == ClassKind.INTERFACE
val IrClass.isClass get() = kind == ClassKind.CLASS val IrClass.isClass get() = kind == ClassKind.CLASS
val IrClass.isObject get() = kind == ClassKind.OBJECT val IrClass.isObject get() = kind == ClassKind.OBJECT
val IrClass.isAnonymousObject get() = isClass && name == SpecialNames.NO_NAME_PROVIDED val IrClass.isAnonymousObject get() = isClass && name == SpecialNames.NO_NAME_PROVIDED
val IrClass.isNonCompanionObject: Boolean get() = isObject && !isCompanion
val IrDeclarationWithName.fqNameWhenAvailable: FqName? val IrDeclarationWithName.fqNameWhenAvailable: FqName?
get() = when (val parent = parent) { get() = when (val parent = parent) {
is IrDeclarationWithName -> parent.fqNameWhenAvailable?.child(name) is IrDeclarationWithName -> parent.fqNameWhenAvailable?.child(name)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.util package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
@@ -21,6 +22,8 @@ open class StubGeneratorExtensions {
open fun isPropertyWithPlatformField(descriptor: PropertyDescriptor): Boolean = false open fun isPropertyWithPlatformField(descriptor: PropertyDescriptor): Boolean = false
open fun isStaticFunction(descriptor: FunctionDescriptor): Boolean = false
open val enhancedNullability: EnhancedNullability open val enhancedNullability: EnhancedNullability
get() = EnhancedNullability get() = EnhancedNullability