JVM IR: support optimized callable reference superclasses
See KT-27362. #KT-37334 Fixed
This commit is contained in:
@@ -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<ClassDescriptor> =
|
||||
lazy { createClass(packageFragment, className) }
|
||||
|
||||
|
||||
@@ -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<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
|
||||
if (!state.getClassBuilderMode().generateBodies) return;
|
||||
|
||||
boolean generateClassIntCtorCall = state.getJvmRuntimeTypes().getGenerateOptimizedCallableReferenceSuperClasses();
|
||||
boolean generateClassIntCtorCall = state.getGenerateOptimizedCallableReferenceSuperClasses();
|
||||
|
||||
InstructionAdapter iv = createOrGetClInitCodegen().v;
|
||||
iv.iconst(delegatedProperties.size());
|
||||
|
||||
@@ -216,11 +216,14 @@ class GenerationState private constructor(
|
||||
!configuration.getBoolean(JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL)
|
||||
IntrinsicMethods(target, canReplaceStdlibRuntimeApiBehavior, shouldUseConsistentEquals)
|
||||
}
|
||||
val generateOptimizedCallableReferenceSuperClasses =
|
||||
languageVersionSettings.apiVersion >= 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
|
||||
|
||||
@@ -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()
|
||||
|
||||
+76
-56
@@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+74
-30
@@ -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<IrValueParameter>) {
|
||||
@@ -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.
|
||||
|
||||
+7
-5
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user