From 2d5e6bb90b661f332d9328340c15a2cd20b90235 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 26 Jun 2020 12:40:06 +0300 Subject: [PATCH] [FIR2IR] Introduce lazy properties --- .../fir/backend/Fir2IrDeclarationStorage.kt | 30 +++- .../kotlin/fir/lazy/Fir2IrLazyClass.kt | 3 +- .../kotlin/fir/lazy/Fir2IrLazyProperty.kt | 168 ++++++++++++++++++ 3 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt index b33fabf76fe..d48797197e4 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyConstructor +import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyProperty import org.jetbrains.kotlin.fir.lazy.Fir2IrLazySimpleFunction import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.* @@ -494,7 +495,7 @@ class Fir2IrDeclarationStorage( return symbolTable.declareSimpleFunction(signature, { Fir2IrSimpleFunctionSymbol(signature, containerSource) }, factory) } - private fun createIrPropertyAccessor( + internal fun createIrPropertyAccessor( propertyAccessor: FirPropertyAccessor?, property: FirProperty, correspondingProperty: IrProperty, @@ -560,7 +561,7 @@ class Fir2IrDeclarationStorage( } } - private fun IrProperty.createBackingField( + internal fun IrProperty.createBackingField( property: FirProperty, origin: IrDeclarationOrigin, descriptor: PropertyDescriptor, @@ -589,7 +590,7 @@ class Fir2IrDeclarationStorage( } } - private val FirProperty.fieldVisibility: Visibility + internal val FirProperty.fieldVisibility: Visibility get() = when { isLateInit -> setter?.visibility ?: status.visibility isConst -> status.visibility @@ -908,14 +909,35 @@ class Fir2IrDeclarationStorage( propertyCache[fir]?.let { return it.symbol } val signature = signatureComposer.composeSignature(fir) val irParent = findIrParent(fir) + val parentOrigin = (irParent as? IrDeclaration)?.origin ?: IrDeclarationOrigin.DEFINED if (signature != null) { symbolTable.referencePropertyIfAny(signature)?.let { irPropertySymbol -> val irProperty = irPropertySymbol.owner propertyCache[fir] = irProperty return irPropertySymbol } + // TODO: package fragment members (?) + if (irParent is Fir2IrLazyClass) { + if (parentOrigin == IrDeclarationOrigin.DEFINED) { + throw AssertionError() + } + val symbol = Fir2IrPropertySymbol(signature, fir.containerSource) + val irProperty = fir.convertWithOffsets { startOffset, endOffset -> + symbolTable.declareProperty(signature, { symbol }) { + val isFakeOverride = + firVariableSymbol is FirPropertySymbol && firVariableSymbol.isFakeOverride && + firVariableSymbol.callableId != firVariableSymbol.overriddenSymbol?.callableId + Fir2IrLazyProperty( + components, startOffset, endOffset, parentOrigin, fir, symbol, isFakeOverride + ).apply { + parent = irParent + } + } + } + propertyCache[fir] = irProperty + return symbol + } } - val parentOrigin = (irParent as? IrDeclaration)?.origin ?: IrDeclarationOrigin.DEFINED createIrProperty(fir, irParent, origin = parentOrigin).apply { setAndModifyParent(irParent) }.symbol diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt index 1429e678073..9fc2a344eb9 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt @@ -145,8 +145,7 @@ class Fir2IrLazyClass( processedNames += declaration.name scope.processPropertiesByName(declaration.name) { if (it is FirPropertySymbol) { - // TODO: replace with getIrFieldOrPropertySymbol(it).owner with lazy property generation - result += declarationStorage.createIrProperty(it.fir, irParent = this, origin = origin) + result += declarationStorage.getIrPropertyOrFieldSymbol(it).owner as IrProperty } } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt new file mode 100644 index 00000000000..65446b651a3 --- /dev/null +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt @@ -0,0 +1,168 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.lazy + +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.fir.backend.Fir2IrComponents +import org.jetbrains.kotlin.fir.backend.toIrConst +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter +import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter +import org.jetbrains.kotlin.fir.expressions.FirConstExpression +import org.jetbrains.kotlin.fir.symbols.Fir2IrPropertySymbol +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.lazy.lazyVar +import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl +import org.jetbrains.kotlin.ir.types.IrErrorType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.isInterface +import org.jetbrains.kotlin.ir.visitors.IrElementTransformer +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.name.Name + +class Fir2IrLazyProperty( + components: Fir2IrComponents, + startOffset: Int, + endOffset: Int, + origin: IrDeclarationOrigin, + fir: FirProperty, + symbol: Fir2IrPropertySymbol, + override val isFakeOverride: Boolean +) : AbstractFir2IrLazyDeclaration( + components, startOffset, endOffset, origin, fir, symbol +), IrProperty { + init { + symbol.bind(this) + classifierStorage.preCacheTypeParameters(fir) + typeParameters = emptyList() + } + + @ObsoleteDescriptorBasedAPI + override val descriptor: PropertyDescriptor + get() = super.descriptor as PropertyDescriptor + + override val symbol: Fir2IrPropertySymbol + get() = super.symbol as Fir2IrPropertySymbol + + override val isVar: Boolean + get() = fir.isVar + + override val isConst: Boolean + get() = fir.isConst + + override val isLateinit: Boolean + get() = fir.isLateInit + + override val isDelegated: Boolean + get() = fir.delegate != null + + override val isExternal: Boolean + get() = fir.isExternal + + override val isExpect: Boolean + get() = fir.isExpect + + override val name: Name + get() = fir.name + + override var visibility: Visibility + get() = fir.visibility + set(_) { + throw AssertionError("Mutating Fir2Ir lazy elements is not possible") + } + + override val modality: Modality + get() = fir.modality!! + + private val type: IrType by lazy { + with(typeConverter) { fir.returnTypeRef.toIrType() } + } + + @OptIn(ObsoleteDescriptorBasedAPI::class) + override var backingField: IrField? by lazyVar { + // TODO: this checks are very preliminary, FIR resolve should determine backing field presence itself + val parent = parent + when { + !fir.isConst && (fir.modality == Modality.ABSTRACT || parent is IrClass && parent.isInterface) -> { + null + } + fir.initializer != null || fir.getter is FirDefaultPropertyGetter || fir.isVar && fir.setter is FirDefaultPropertySetter -> { + with(declarationStorage) { + createBackingField( + fir, IrDeclarationOrigin.PROPERTY_BACKING_FIELD, descriptor, + fir.fieldVisibility, fir.name, fir.isVal, fir.initializer, + type + ).also { field -> + val initializer = fir.initializer + if (initializer is FirConstExpression<*>) { + // TODO: Normally we shouldn't have error type here + val constType = with(typeConverter) { initializer.typeRef.toIrType().takeIf { it !is IrErrorType } ?: type } + field.initializer = IrExpressionBodyImpl(initializer.toIrConst(constType)) + } + } + } + } + fir.delegate != null -> { + with(declarationStorage) { + createBackingField( + fir, IrDeclarationOrigin.PROPERTY_DELEGATE, descriptor, + fir.fieldVisibility, Name.identifier("${fir.name}\$delegate"), true, fir.delegate + ) + } + } + else -> { + null + } + }?.apply { + this.parent = this@Fir2IrLazyProperty.parent + } + } + + override var getter: IrSimpleFunction? by lazyVar { + declarationStorage.createIrPropertyAccessor( + fir.getter, fir, this, type, parent, parent as? IrClass, false, + when { + origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB -> origin + fir.delegate != null -> IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR + fir.getter is FirDefaultPropertyGetter -> IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR + else -> origin + }, + startOffset, endOffset + ) + } + + override var setter: IrSimpleFunction? by lazyVar { + if (!fir.isVar) return@lazyVar null + declarationStorage.createIrPropertyAccessor( + fir.setter, fir, this, type, parent, parent as? IrClass, true, + when { + fir.delegate != null -> IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR + fir.setter is FirDefaultPropertySetter -> IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR + else -> origin + }, + startOffset, endOffset + ) + } + + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitProperty(this, data) + } + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + backingField?.accept(visitor, data) + getter?.accept(visitor, data) + setter?.accept(visitor, data) + } + + override fun transformChildren(transformer: IrElementTransformer, data: D) { + backingField = backingField?.transform(transformer, data) as? IrField + getter = getter?.run { transform(transformer, data) as IrSimpleFunction } + setter = setter?.run { transform(transformer, data) as IrSimpleFunction } + } +} \ No newline at end of file