From 774fc32f3e9ab929d9a6fe5ed374b53f92230168 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 31 Mar 2020 22:04:18 +0200 Subject: [PATCH] JVM IR: support optimized callable reference superclasses See KT-27362. #KT-37334 Fixed --- .../kotlin/codegen/JvmRuntimeTypes.kt | 6 +- .../kotlin/codegen/MemberCodegen.java | 4 +- .../kotlin/codegen/state/GenerationState.kt | 5 +- .../kotlin/backend/jvm/JvmSymbols.kt | 37 +++++ .../jvm/lower/FunctionReferenceLowering.kt | 132 ++++++++++-------- .../jvm/lower/PropertyReferenceLowering.kt | 104 ++++++++++---- .../optimizedSuperclasses_after.kt | 12 +- 7 files changed, 200 insertions(+), 100 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index 293ff2a0b29..9cb119d92e5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.builtins.createFunctionType import org.jetbrains.kotlin.codegen.coroutines.coroutinesJvmInternalPackageFqName import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView import org.jetbrains.kotlin.codegen.coroutines.isSuspendLambdaOrLocalFunction -import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.isReleaseCoroutines @@ -29,15 +28,12 @@ import org.jetbrains.kotlin.types.KotlinType class JvmRuntimeTypes( module: ModuleDescriptor, private val languageVersionSettings: LanguageVersionSettings, - forceNoOptimizedCallableReferences: Boolean + private val generateOptimizedCallableReferenceSuperClasses: Boolean ) { private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal")) private val kotlinCoroutinesJvmInternalPackage = MutablePackageFragmentDescriptor(module, languageVersionSettings.coroutinesJvmInternalPackageFqName()) - val generateOptimizedCallableReferenceSuperClasses = - languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4 && !forceNoOptimizedCallableReferences - private fun internal(className: String, packageFragment: PackageFragmentDescriptor = kotlinJvmInternalPackage): Lazy = lazy { createClass(packageFragment, className) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java index 22511e1253e..2c233fa850e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java @@ -16,11 +16,9 @@ import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper; import org.jetbrains.kotlin.codegen.inline.NameGenerator; import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages; -import org.jetbrains.kotlin.codegen.inline.SourceMapper; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt; -import org.jetbrains.kotlin.config.ApiVersion; import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.AnnotatedImpl; @@ -644,7 +642,7 @@ public abstract class MemberCodegen= ApiVersion.KOTLIN_1_4 && + !configuration.getBoolean(JVMConfigurationKeys.NO_OPTIMIZED_CALLABLE_REFERENCES) val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this) val globalInlineContext: GlobalInlineContext = GlobalInlineContext(diagnostics) val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this) val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes( - module, configuration.languageVersionSettings, configuration.getBoolean(JVMConfigurationKeys.NO_OPTIMIZED_CALLABLE_REFERENCES) + module, configuration.languageVersionSettings, generateOptimizedCallableReferenceSuperClasses ) val factory: ClassFileFactory private var duplicateSignatureFactory: BuilderFactoryForDuplicateSignatureDiagnostics? = null diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index cd687d1790c..d071214c648 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -50,6 +50,8 @@ class JvmSymbols( private val irBuiltIns = context.irBuiltIns + private val generateOptimizedCallableReferenceSuperClasses = context.state.generateOptimizedCallableReferenceSuperClasses + private val nullPointerExceptionClass: IrClassSymbol = createClass(FqName("java.lang.NullPointerException")) { klass -> klass.addConstructor().apply { @@ -307,6 +309,26 @@ class JvmSymbols( val functionReferenceGetName: IrSimpleFunctionSymbol = functionReference.functionByName("getName") val functionReferenceGetOwner: IrSimpleFunctionSymbol = functionReference.functionByName("getOwner") + val functionReferenceImpl: IrClassSymbol = + createClass(FqName("kotlin.jvm.internal.FunctionReferenceImpl"), classModality = Modality.OPEN) { klass -> + klass.superTypes = listOf(functionReference.defaultType) + + if (generateOptimizedCallableReferenceSuperClasses) { + for (hasBoundReceiver in listOf(false, true)) { + klass.addConstructor().apply { + addValueParameter("arity", irBuiltIns.intType) + if (hasBoundReceiver) { + addValueParameter("receiver", irBuiltIns.anyNType) + } + addValueParameter("owner", javaLangClass.starProjectedType) + addValueParameter("name", irBuiltIns.stringType) + addValueParameter("signature", irBuiltIns.stringType) + addValueParameter("flags", irBuiltIns.intType) + } + } + } + } + fun getFunction(parameterCount: Int): IrClassSymbol = symbolTable.referenceClass(builtIns.getFunction(parameterCount)) @@ -379,6 +401,21 @@ class JvmSymbols( addValueParameter("name", irBuiltIns.stringType) addValueParameter("string", irBuiltIns.stringType) } + + if (generateOptimizedCallableReferenceSuperClasses) { + for (hasBoundReceiver in listOf(false, true)) { + klass.addConstructor().apply { + if (hasBoundReceiver) { + addValueParameter("receiver", irBuiltIns.anyNType) + } + addValueParameter("owner", javaLangClass.starProjectedType) + addValueParameter("name", irBuiltIns.stringType) + addValueParameter("signature", irBuiltIns.stringType) + addValueParameter("flags", irBuiltIns.intType) + } + } + } + klass.superTypes += getPropertyReferenceClass(mutable, parameterCount, false).defaultType } else { klass.addConstructor() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 15c90c045c0..a87b5459ce5 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -11,14 +11,10 @@ import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor import org.jetbrains.kotlin.backend.common.ir.isSuspend import org.jetbrains.kotlin.backend.common.ir.moveBodyTo -import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin -import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator -import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder -import org.jetbrains.kotlin.backend.jvm.ir.irArray -import org.jetbrains.kotlin.backend.jvm.ir.isLambda +import org.jetbrains.kotlin.backend.jvm.ir.* import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET @@ -123,8 +119,14 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) context.ir.symbols.getJvmFunctionClass(argumentTypes.size) private val superMethod = functionSuperClass.functions.single { it.owner.modality == Modality.ABSTRACT } + private val useOptimizedSuperClass = + context.state.generateOptimizedCallableReferenceSuperClasses private val superType = - samSuperType ?: (if (isLambda) context.ir.symbols.lambdaClass else context.ir.symbols.functionReference).defaultType + samSuperType ?: when { + isLambda -> context.ir.symbols.lambdaClass + useOptimizedSuperClass -> context.ir.symbols.functionReferenceImpl + else -> context.ir.symbols.functionReference + }.defaultType private val functionReferenceClass = buildClass { setSourceRange(irFunctionReference) @@ -167,10 +169,16 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) } else null ) - if (!isLambda && samSuperType == null) { - createGetSignatureMethod(this@run.irSymbols.functionReferenceGetSignature.owner) - createGetNameMethod(this@run.irSymbols.functionReferenceGetName.owner) - createGetOwnerMethod(this@run.irSymbols.functionReferenceGetOwner.owner) + if (!isLambda && samSuperType == null && !useOptimizedSuperClass) { + createLegacyMethodOverride(irSymbols.functionReferenceGetSignature.owner) { + generateSignature() + } + createLegacyMethodOverride(irSymbols.functionReferenceGetName.owner) { + irString(callee.originalName.asString()) + } + createLegacyMethodOverride(irSymbols.functionReferenceGetOwner.owner) { + calculateOwner(callee.parent, backendContext) + } } +functionReferenceClass @@ -199,25 +207,43 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) // Super constructor: // - For SAM references, the super class is Any - // - For function references with bound receivers, accepts arity and receiver - // - For lambdas and function references without bound receivers, accepts arity + // - For lambdas, accepts arity + // - For optimized function references (1.4+), accepts: + // arity, [receiver], owner, name, signature, flags + // - For unoptimized function references, accepts: + // arity, [receiver] val constructor = if (samSuperType != null) { context.irBuiltIns.anyClass.owner.constructors.single() } else { + val expectedArity = + if (isLambda) 1 + else 1 + (if (boundReceiver != null) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0) superType.getClass()!!.constructors.single { - it.valueParameters.size == if (boundReceiver != null) 2 else 1 + it.valueParameters.size == expectedArity } } - body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) { - +irDelegatingConstructorCall(constructor).apply { - if (samSuperType == null) { - putValueArgument(0, irInt(argumentTypes.size + if (irFunctionReference.isSuspend) 1 else 0)) - if (boundReceiver != null) - putValueArgument(1, irGet(valueParameters.first())) + body = context.createJvmIrBuilder(symbol).run { + irBlockBody(startOffset, endOffset) { + +irDelegatingConstructorCall(constructor).apply { + if (samSuperType == null) { + var index = 0 + putValueArgument(index++, irInt(argumentTypes.size + if (irFunctionReference.isSuspend) 1 else 0)) + if (boundReceiver != null) { + putValueArgument(index++, irGet(valueParameters.first())) + } + if (!isLambda && useOptimizedSuperClass) { + val owner = calculateOwnerKClass(callee.parent, backendContext) + putValueArgument(index++, kClassToJavaClass(owner, backendContext)) + putValueArgument(index++, irString(callee.originalName.asString())) + putValueArgument(index++, generateSignature()) + // TODO: use correct parents for adapted function references + putValueArgument(index, irInt(if (callee.parent.let { it is IrClass && it.isFileClass }) 1 else 0)) + } + } } + +IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, context.irBuiltIns.unitType) } - +IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, context.irBuiltIns.unitType) } } @@ -320,37 +346,28 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) private val IrFunction.originalName: Name get() = (metadata as? MetadataSource.Function)?.descriptor?.name ?: name - private fun createGetSignatureMethod(superFunction: IrSimpleFunction): IrSimpleFunction = buildOverride(superFunction).apply { - body = context.createJvmIrBuilder(symbol, startOffset, endOffset).run { - irExprBody(irCall(backendContext.ir.symbols.signatureStringIntrinsic).apply { - putValueArgument( - 0, - //don't pass receivers otherwise LocalDeclarationLowering will create additional captured parameters - IrFunctionReferenceImpl( - UNDEFINED_OFFSET, - UNDEFINED_OFFSET, - irFunctionReference.type, - irFunctionReference.symbol, - 0, - irFunctionReference.reflectionTarget, - null - ) + private fun JvmIrBuilder.generateSignature(): IrExpression = + irCall(backendContext.ir.symbols.signatureStringIntrinsic).apply { + putValueArgument( + 0, + //don't pass receivers otherwise LocalDeclarationLowering will create additional captured parameters + IrFunctionReferenceImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, irFunctionReference.type, irFunctionReference.symbol, 0, + irFunctionReference.reflectionTarget, null ) - }) + ) } - } - private fun createGetNameMethod(superFunction: IrSimpleFunction): IrSimpleFunction = buildOverride(superFunction).apply { - body = context.createIrBuilder(symbol, startOffset, endOffset).run { - irExprBody(irString(callee.originalName.asString())) + private fun createLegacyMethodOverride( + superFunction: IrSimpleFunction, + generator: JvmIrBuilder.() -> IrExpression + ): IrSimpleFunction = + buildOverride(superFunction).apply { + body = context.createJvmIrBuilder(symbol, startOffset, endOffset).run { + irExprBody(generator()) + } } - } - private fun createGetOwnerMethod(superFunction: IrSimpleFunction): IrSimpleFunction = buildOverride(superFunction).apply { - body = context.createIrBuilder(symbol, startOffset, endOffset).run { - irExprBody(calculateOwner(callee.parent, this@FunctionReferenceLowering.context)) - } - } } companion object { @@ -359,7 +376,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) startOffset, endOffset, context.irBuiltIns.kClassClass.starProjectedType, context.irBuiltIns.kClassClass, classType ) - private fun IrBuilderWithScope.kClassToJavaClass(kClassReference: IrExpression, context: JvmBackendContext) = + internal fun IrBuilderWithScope.kClassToJavaClass(kClassReference: IrExpression, context: JvmBackendContext) = irGet(context.ir.symbols.javaLangClass.starProjectedType, null, context.ir.symbols.kClassJava.owner.getter!!.symbol).apply { extensionReceiver = kClassReference } @@ -368,15 +385,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) kClassToJavaClass(kClassReference(classType), context) internal fun IrBuilderWithScope.calculateOwner(irContainer: IrDeclarationParent, context: JvmBackendContext): IrExpression { - val classType = - if (irContainer is IrClass) irContainer.defaultType - else { - // For built-in members (i.e. top level `toString`) we generate reference to an internal class for an owner. - // This allows kotlin-reflect to understand that this is a built-in intrinsic which has no real declaration, - // and construct a special KCallable object. - context.ir.symbols.intrinsicsKotlinClass.defaultType - } - val kClass = kClassReference(classType) + val kClass = calculateOwnerKClass(irContainer, context) if ((irContainer as? IrClass)?.isFileClass != true && irContainer !is IrPackageFragment) return kClass @@ -389,5 +398,16 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) putValueArgument(1, irString(context.state.moduleName)) } } + + internal fun IrBuilderWithScope.calculateOwnerKClass(irContainer: IrDeclarationParent, context: JvmBackendContext): IrExpression = + kClassReference( + if (irContainer is IrClass) irContainer.defaultType + else { + // For built-in members (i.e. top level `toString`) we generate reference to an internal class for an owner. + // This allows kotlin-reflect to understand that this is a built-in intrinsic which has no real declaration, + // and construct a special KCallable object. + context.ir.symbols.intrinsicsKotlinClass.defaultType + } + ) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt index 4c8bf167e88..d934f30ee18 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -14,20 +14,22 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.backend.jvm.ir.JvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf +import org.jetbrains.kotlin.backend.jvm.lower.FunctionReferenceLowering.Companion.calculateOwner +import org.jetbrains.kotlin.backend.jvm.lower.FunctionReferenceLowering.Companion.calculateOwnerKClass +import org.jetbrains.kotlin.backend.jvm.lower.FunctionReferenceLowering.Companion.kClassToJavaClass import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.addField -import org.jetbrains.kotlin.ir.builders.declarations.addFunction -import org.jetbrains.kotlin.ir.builders.declarations.buildClass -import org.jetbrains.kotlin.ir.builders.declarations.buildField +import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.createType import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl @@ -84,6 +86,9 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class private val kPropertiesFieldType = context.ir.symbols.array.createType(false, listOf(makeTypeProjection(kPropertyStarType, Variance.OUT_VARIANCE))) + private val useOptimizedSuperClass = + context.state.generateOptimizedCallableReferenceSuperClasses + private val IrMemberAccessExpression.propertyContainer: IrDeclarationParent get() { var current: IrDeclaration = getter?.owner ?: field?.owner ?: error("Property without getter or field: ${dump()}") @@ -93,9 +98,10 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class } private fun IrBuilderWithScope.buildReflectedContainerReference(expression: IrMemberAccessExpression): IrExpression = - with(FunctionReferenceLowering) { - calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context) - } + calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context) + + private fun JvmIrBuilder.buildReflectedContainerReferenceKClass(expression: IrMemberAccessExpression): IrExpression = + calculateOwnerKClass(expression.propertyContainer, backendContext) private fun IrBuilderWithScope.computeSignatureString(expression: IrCallableReference): IrExpression { return expression.getter?.let { getter -> @@ -149,7 +155,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class private class PropertyReferenceKind( val interfaceSymbol: IrClassSymbol, - val reflectedSymbol: IrClassSymbol, + val implSymbol: IrClassSymbol, val wrapper: IrFunction ) @@ -222,7 +228,8 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class val referenceKind = propertyReferenceKindFor(expression) return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { irCall(referenceKind.wrapper).apply { - putValueArgument(0, irCall(referenceKind.reflectedSymbol.constructors.single()).apply { + val constructor = referenceKind.implSymbol.constructors.single { it.owner.valueParameters.size == 3 } + putValueArgument(0, irCall(constructor).apply { putValueArgument(0, buildReflectedContainerReference(expression)) putValueArgument(1, irString(expression.symbol.descriptor.name.asString())) putValueArgument(2, computeSignatureString(expression)) @@ -234,11 +241,8 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class // Create an instance of KProperty that overrides the get() and set() methods to directly call getX() and setX() on the object. // This is (relatively) fast, but space-inefficient. Also, the instances can store bound receivers in their fields. Example: // - // class C$property$0 : PropertyReference0 { - // constructor(boundReceiver: C) : super(boundReceiver) - // override val name = "property" - // override fun getOwner() = C::class - // override fun getSignature() = "getProperty()LType;" + // class C$property$0 : PropertyReference0Impl { + // constructor(boundReceiver: C) : super(boundReceiver, C::class.java, "property", "getProperty()LType;", 0) // override fun get(): T = receiver.property // override fun set(value: T) { receiver.property = value } // } @@ -261,7 +265,8 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class } private fun createKPropertySubclass(expression: IrCallableReference): IrClass { - val superClass = propertyReferenceKindFor(expression).interfaceSymbol.owner + val kind = propertyReferenceKindFor(expression) + val superClass = if (useOptimizedSuperClass) kind.implSymbol.owner else kind.interfaceSymbol.owner val referenceClass = buildClass { setSourceRange(expression) name = SpecialNames.NO_NAME_PROVIDED @@ -269,27 +274,23 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class visibility = Visibilities.LOCAL }.apply { parent = irClass - superTypes += IrSimpleTypeImpl(superClass.symbol, false, listOf(), listOf()) + superTypes = listOf(superClass.defaultType) createImplicitParameterDeclarationWithWrappedDescriptor() }.copyAttributes(expression) - // See propertyReferenceKindFor -- only one of them could ever be present. - val numOfSuperArgs = if (expression.dispatchReceiver != null || expression.extensionReceiver != null) 1 else 0 - val superConstructor = superClass.constructors.single { it.valueParameters.size == numOfSuperArgs } - val backingFieldFromSuper = superClass.properties.single { it.name.asString() == "receiver" }.backingField!! - val getName = superClass.functions.single { it.name.asString() == "getName" } - val getOwner = superClass.functions.single { it.name.asString() == "getOwner" } - val getSignature = superClass.functions.single { it.name.asString() == "getSignature" } - val get = superClass.functions.find { it.name.asString() == "get" } - val set = superClass.functions.find { it.name.asString() == "set" } - val invoke = superClass.functions.find { it.name.asString() == "invoke" } + addConstructor(expression, referenceClass, superClass) - referenceClass.addSimpleDelegatingConstructor(superConstructor, context.irBuiltIns, isPrimary = true) - referenceClass.addOverride(getName) { irString(expression.symbol.descriptor.name.asString()) } - referenceClass.addOverride(getOwner) { buildReflectedContainerReference(expression) } - referenceClass.addOverride(getSignature) { computeSignatureString(expression) } + if (!useOptimizedSuperClass) { + val getName = superClass.functions.single { it.name.asString() == "getName" } + val getOwner = superClass.functions.single { it.name.asString() == "getOwner" } + val getSignature = superClass.functions.single { it.name.asString() == "getSignature" } + referenceClass.addOverride(getName) { irString(expression.symbol.descriptor.name.asString()) } + referenceClass.addOverride(getOwner) { buildReflectedContainerReference(expression) } + referenceClass.addOverride(getSignature) { computeSignatureString(expression) } + } val receiverField = referenceClass.addField { + val backingFieldFromSuper = superClass.properties.single { it.name.asString() == "receiver" }.backingField!! name = backingFieldFromSuper.name isFakeOverride = true origin = IrDeclarationOrigin.FAKE_OVERRIDE @@ -299,6 +300,10 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class visibility = backingFieldFromSuper.visibility } + val get = superClass.functions.find { it.name.asString() == "get" } + val set = superClass.functions.find { it.name.asString() == "set" } + val invoke = superClass.functions.find { it.name.asString() == "invoke" } + val field = expression.field?.owner if (field == null) { fun IrBuilderWithScope.setCallArguments(call: IrCall, arguments: List) { @@ -356,6 +361,45 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class } return referenceClass } + + private fun addConstructor(expression: IrCallableReference, referenceClass: IrClass, superClass: IrClass) { + // See propertyReferenceKindFor -- only one of them could ever be present. + val hasBoundReceiver = expression.dispatchReceiver != null || expression.extensionReceiver != null + val numOfSuperArgs = + (if (hasBoundReceiver) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0) + val superConstructor = superClass.constructors.single { it.valueParameters.size == numOfSuperArgs } + + if (!useOptimizedSuperClass) { + referenceClass.addSimpleDelegatingConstructor(superConstructor, context.irBuiltIns, isPrimary = true) + return + } + + referenceClass.addConstructor { + origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE + isPrimary = true + }.apply { + if (hasBoundReceiver) { + addValueParameter("receiver", context.irBuiltIns.anyNType) + } + body = context.createJvmIrBuilder(symbol).run { + irBlockBody(startOffset, endOffset) { + +irDelegatingConstructorCall(superConstructor).apply { + var index = 0 + if (hasBoundReceiver) { + putValueArgument(index++, irGet(valueParameters.first())) + } + val callee = expression.symbol.owner as IrDeclaration + val owner = buildReflectedContainerReferenceKClass(expression) + putValueArgument(index++, kClassToJavaClass(owner, backendContext)) + putValueArgument(index++, irString(expression.symbol.descriptor.name.asString())) + putValueArgument(index++, computeSignatureString(expression)) + putValueArgument(index, irInt(if (callee.parent.let { it is IrClass && it.isFileClass }) 1 else 0)) + } + +IrInstanceInitializerCallImpl(startOffset, endOffset, referenceClass.symbol, context.irBuiltIns.unitType) + } + } + } + } }) // Put the new field at the beginning so that static delegated properties with initializers work correctly. diff --git a/compiler/testData/codegen/box/callableReference/optimizedSuperclasses_after.kt b/compiler/testData/codegen/box/callableReference/optimizedSuperclasses_after.kt index af542669d4a..e7b42eee1ff 100644 --- a/compiler/testData/codegen/box/callableReference/optimizedSuperclasses_after.kt +++ b/compiler/testData/codegen/box/callableReference/optimizedSuperclasses_after.kt @@ -1,6 +1,4 @@ // TARGET_BACKEND: JVM -// IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME class A { @@ -9,6 +7,8 @@ class A { } val topLevelProperty: Int = 0 +fun A.extensionFunction() {} +val A.extensionProperty: String get() = "" fun check(reference: Any, expected: String, message: String) { val actual = reference.javaClass.declaredMethods.map { it.name }.sorted().toString() @@ -21,9 +21,11 @@ fun box(): String { check(A::memberFunction, "[invoke, invoke]", "unbound function reference") check(A()::memberFunction, "[invoke, invoke]", "bound function reference") - check(::topLevelProperty, "[get]", "unbound property reference 0") - check(A::memberProperty, "[get]", "unbound property reference 1") - check(A()::memberProperty, "[get]", "bound property reference 1") + check(::topLevelProperty, "[get]", "unbound top-level property reference 0") + check(A::memberProperty, "[get]", "unbound member property reference 1") + check(A()::memberProperty, "[get]", "bound member property reference 1") + check(A::extensionProperty, "[get]", "unbound extension property reference 1") + check(A()::extensionProperty, "[get]", "bound extension property reference 1") return "OK" }