From a943cdadef7165af02ab067983819827dff24969 Mon Sep 17 00:00:00 2001 From: pyos Date: Wed, 30 Jun 2021 18:19:57 +0200 Subject: [PATCH] JVM_IR: support generation of bound reflected property references Not currently used though, since only references for delegated properties' accessors are reflected, and those are unbound. --- .../backend/jvm/lower/AssertionLowering.kt | 9 +- .../backend/jvm/lower/EnumClassLowering.kt | 2 +- .../jvm/lower/FunctionReferenceLowering.kt | 28 +++--- .../jvm/lower/PropertyReferenceLowering.kt | 85 +++++++++---------- 4 files changed, 56 insertions(+), 68 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AssertionLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AssertionLowering.kt index f67c7ba3aa7..757731b8a07 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AssertionLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AssertionLowering.kt @@ -120,11 +120,6 @@ private class AssertionLowering(private val context: JvmBackendContext) : get() = name.asString() == "assert" && getPackageFragment()?.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME } -private fun IrBuilderWithScope.getJavaClass(backendContext: JvmBackendContext, irClass: IrClass) = - with(FunctionReferenceLowering) { - javaClassReference(irClass.defaultType, backendContext) - } - fun IrClass.buildAssertionsDisabledField(backendContext: JvmBackendContext, topLevelClass: IrClass) = factory.buildField { name = Name.identifier(ASSERTIONS_DISABLED_FIELD_NAME) @@ -138,7 +133,9 @@ fun IrClass.buildAssertionsDisabledField(backendContext: JvmBackendContext, topL field.initializer = backendContext.createJvmIrBuilder(this.symbol).run { at(field) irExprBody(irNot(irCall(irSymbols.desiredAssertionStatus).apply { - dispatchReceiver = getJavaClass(backendContext, topLevelClass) + dispatchReceiver = with(FunctionReferenceLowering) { + javaClassReference(topLevelClass.defaultType) + } })) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt index 7312dc09c84..ce05aa1836d 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt @@ -162,7 +162,7 @@ private class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringP IrSyntheticBodyKind.ENUM_VALUEOF -> irCall(backendContext.ir.symbols.enumValueOfFunction).apply { putValueArgument(0, with(FunctionReferenceLowering) { - javaClassReference(irClass.defaultType, backendContext) + javaClassReference(irClass.defaultType) }) putValueArgument(1, irGet(declaration.valueParameters[0])) } 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 fec70c9a8e8..3690ff5e78c 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 @@ -519,7 +519,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) irString(callee.originalName.asString()) } createLegacyMethodOverride(irSymbols.functionReferenceGetOwner.owner) { - calculateOwner(callee.parent, backendContext) + calculateOwner(callee.parent) } } @@ -632,8 +632,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) } if (!isLambda && useOptimizedSuperClass) { val callableReferenceTarget = adaptedReferenceOriginalTarget ?: callee - val owner = calculateOwnerKClass(callableReferenceTarget.parent, backendContext) - call.putValueArgument(index++, kClassToJavaClass(owner, backendContext)) + val owner = calculateOwnerKClass(callableReferenceTarget.parent) + call.putValueArgument(index++, kClassToJavaClass(owner)) call.putValueArgument(index++, irString(callableReferenceTarget.originalName.asString())) call.putValueArgument(index++, generateSignature(callableReferenceTarget.symbol)) call.putValueArgument(index, irInt(getFunctionReferenceFlags(callableReferenceTarget))) @@ -808,31 +808,31 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) startOffset, endOffset, context.irBuiltIns.kClassClass.starProjectedType, context.irBuiltIns.kClassClass, classType ) - internal fun IrBuilderWithScope.kClassToJavaClass(kClassReference: IrExpression, context: JvmBackendContext) = - irGet(context.ir.symbols.javaLangClass.starProjectedType, null, context.ir.symbols.kClassJava.owner.getter!!.symbol).apply { + internal fun JvmIrBuilder.kClassToJavaClass(kClassReference: IrExpression) = + irGet(irSymbols.javaLangClass.starProjectedType, null, irSymbols.kClassJava.owner.getter!!.symbol).apply { extensionReceiver = kClassReference } - internal fun IrBuilderWithScope.javaClassReference(classType: IrType, context: JvmBackendContext) = - kClassToJavaClass(kClassReference(classType), context) + internal fun JvmIrBuilder.javaClassReference(classType: IrType) = + kClassToJavaClass(kClassReference(classType)) - internal fun IrBuilderWithScope.calculateOwner(irContainer: IrDeclarationParent, context: JvmBackendContext): IrExpression { - val kClass = calculateOwnerKClass(irContainer, context) + internal fun JvmIrBuilder.calculateOwner(irContainer: IrDeclarationParent): IrExpression { + val kClass = calculateOwnerKClass(irContainer) if ((irContainer as? IrClass)?.isFileClass != true && irContainer !is IrPackageFragment) return kClass - return irCall(context.ir.symbols.getOrCreateKotlinPackage).apply { - putValueArgument(0, kClassToJavaClass(kClass, context)) + return irCall(irSymbols.getOrCreateKotlinPackage).apply { + putValueArgument(0, kClassToJavaClass(kClass)) // Note that this name is not used in reflection. There should be the name of the referenced declaration's // module instead, but there's no nice API to obtain that name here yet // TODO: write the referenced declaration's module name and use it in reflection - putValueArgument(1, irString(context.state.moduleName)) + putValueArgument(1, irString(backendContext.state.moduleName)) } } - internal fun IrBuilderWithScope.calculateOwnerKClass(irContainer: IrDeclarationParent, context: JvmBackendContext): IrExpression = - kClassReference(getOwnerKClassType(irContainer, context)) + internal fun JvmIrBuilder.calculateOwnerKClass(irContainer: IrDeclarationParent): IrExpression = + kClassReference(getOwnerKClassType(irContainer, backendContext)) internal fun getOwnerKClassType(irContainer: IrDeclarationParent, context: JvmBackendContext): IrType = if (irContainer is IrClass) irContainer.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 15813f45a48..6e099e403a7 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 @@ -15,10 +15,7 @@ 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.codegen.parentClassId -import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder -import org.jetbrains.kotlin.backend.jvm.ir.hasChild -import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf -import org.jetbrains.kotlin.backend.jvm.ir.needsAccessor +import org.jetbrains.kotlin.backend.jvm.ir.* 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 @@ -129,7 +126,7 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem return irCall(signatureStringIntrinsic).apply { putValueArgument(0, reference) } } - private fun IrClass.addOverride(method: IrSimpleFunction, buildBody: IrBuilderWithScope.(List) -> IrExpression) = + private fun IrClass.addOverride(method: IrSimpleFunction, buildBody: JvmIrBuilder.(List) -> IrExpression) = addFunction { setSourceRange(this@addOverride) name = method.name @@ -141,7 +138,7 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem overriddenSymbols += method.symbol dispatchReceiverParameter = thisReceiver!!.copyTo(this) valueParameters = method.valueParameters.map { it.copyTo(this) } - body = context.createIrBuilder(symbol, startOffset, endOffset).run { + body = context.createJvmIrBuilder(symbol, startOffset, endOffset).run { irExprBody(buildBody(listOf(dispatchReceiverParameter!!) + valueParameters)) } } @@ -316,38 +313,43 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem } // Create an instance of KProperty that uses Java reflection to locate the getter and the setter. This kind of reference - // does not support local variables or bound receivers (e.g. `Class()::field`) and is slower, but takes up less space. + // does not support local variables and is slower, but takes up less space in the output binary. // Example: `C::property` -> `Reflection.property1(PropertyReference1Impl(C::class, "property", "getProperty()LType;"))`. private fun createReflectedKProperty(expression: IrCallableReference<*>): IrExpression { - assert(expression.dispatchReceiver == null && expression.extensionReceiver == null) { - // TODO: technically can with `generateOptimizedCallableReferenceSuperClasses` - use the 5-argument constructor - "cannot create a reflected KProperty if the reference has a bound receiver: ${expression.render()}" + val boundReceiver = expression.getBoundReceiver() + if (boundReceiver != null && !useOptimizedSuperClass) { + // Pre-1.4 reflected property reference constructors do not allow bound receivers. + return createSpecializedKProperty(expression) } val referenceKind = propertyReferenceKindFor(expression) - return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { - irCall(referenceKind.wrapper).apply { - val container = expression.propertyContainer - val name = irString(expression.referencedName.asString()) - val signature = computeSignatureString(expression) - val impl = when { - this@PropertyReferenceLowering.context.state.generateOptimizedCallableReferenceSuperClasses -> - irCall(referenceKind.implSymbol.constructors.single { it.owner.valueParameters.size == 4 }).apply { - val kClass = calculateOwnerKClass(container, this@PropertyReferenceLowering.context) - val isPackage = (container is IrClass && container.isFileClass) || container is IrPackageFragment - putValueArgument(0, kClassToJavaClass(kClass, this@PropertyReferenceLowering.context)) - putValueArgument(1, name) - putValueArgument(2, signature) - putValueArgument(3, irInt(if (isPackage) 1 else 0)) - } - else -> - irCall(referenceKind.implSymbol.constructors.single { it.owner.valueParameters.size == 3 }).apply { - putValueArgument(0, calculateOwner(container, this@PropertyReferenceLowering.context)) - putValueArgument(1, name) - putValueArgument(2, signature) - } - } - putValueArgument(0, impl) + return context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { + val arity = when { + boundReceiver != null -> 5 // (receiver, jClass, name, desc, flags) + useOptimizedSuperClass -> 4 // (jClass, name, desc, flags) + else -> 3 // (kClass, name, desc) } + val instance = irCall(referenceKind.implSymbol.constructors.single { it.owner.valueParameters.size == arity }).apply { + fillReflectedPropertyArguments(this, expression, boundReceiver) + } + irCall(referenceKind.wrapper).apply { putValueArgument(0, instance) } + } + } + + private fun JvmIrBuilder.fillReflectedPropertyArguments( + call: IrFunctionAccessExpression, + expression: IrCallableReference<*>, + receiver: IrExpression?, + ) { + val container = expression.propertyContainer + val containerClass = if (useOptimizedSuperClass) kClassToJavaClass(calculateOwnerKClass(container)) else calculateOwner(container) + var index = 0 + receiver?.let { call.putValueArgument(index++, it) } + call.putValueArgument(index++, containerClass) + call.putValueArgument(index++, irString(expression.referencedName.asString())) + call.putValueArgument(index++, computeSignatureString(expression)) + if (useOptimizedSuperClass) { + val isPackage = (container is IrClass && container.isFileClass) || container is IrPackageFragment + call.putValueArgument(index, irInt(if (isPackage) 1 else 0)) } } @@ -395,7 +397,7 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem val getOwner = superClass.functions.single { it.name.asString() == "getOwner" } val getSignature = superClass.functions.single { it.name.asString() == "getSignature" } referenceClass.addOverride(getName) { irString(expression.referencedName.asString()) } - referenceClass.addOverride(getOwner) { calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context) } + referenceClass.addOverride(getOwner) { calculateOwner(expression.propertyContainer) } referenceClass.addOverride(getSignature) { computeSignatureString(expression) } } @@ -475,22 +477,11 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE isPrimary = true }.apply { - if (hasBoundReceiver) { - addValueParameter("receiver", context.irBuiltIns.anyNType) - } + val receiverParameter = if (hasBoundReceiver) addValueParameter("receiver", context.irBuiltIns.anyNType) else null 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 = calculateOwnerKClass(expression.propertyContainer, backendContext) - putValueArgument(index++, kClassToJavaClass(owner, backendContext)) - putValueArgument(index++, irString(expression.referencedName.asString())) - putValueArgument(index++, computeSignatureString(expression)) - putValueArgument(index, irInt(FunctionReferenceLowering.getCallableReferenceTopLevelFlag(callee))) + fillReflectedPropertyArguments(this, expression, receiverParameter?.let(::irGet)) } +IrInstanceInitializerCallImpl(startOffset, endOffset, referenceClass.symbol, context.irBuiltIns.unitType) }