Add accessor symbols & test for property overriding in Java

This commit is contained in:
Mikhail Glukhikh
2019-03-13 16:44:16 +03:00
parent 9abf4062b1
commit cf72b13d84
15 changed files with 201 additions and 50 deletions
@@ -19,9 +19,11 @@ import org.jetbrains.kotlin.fir.java.enhancement.*
import org.jetbrains.kotlin.fir.java.enhancement.EnhancementSignatureParts
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.*
@@ -74,30 +76,43 @@ class JavaClassEnhancementScope(
private fun enhance(
original: ConeVariableSymbol,
name: Name
): FirPropertySymbol {
val firField = (original as FirBasedSymbol<*>).fir as? FirJavaField ?: error("Can't make enhancement for $original")
): ConePropertySymbol {
when (val firElement = (original as FirBasedSymbol<*>).fir) {
is FirJavaField -> {
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firField.annotations)
val newReturnTypeRef = enhanceReturnType(firField, emptyList(), memberContext, null)
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firElement.annotations)
val newReturnTypeRef = enhanceReturnType(firElement, emptyList(), memberContext, null)
val symbol = FirPropertySymbol(original.callableId)
with(firField) {
FirMemberPropertyImpl(
this@JavaClassEnhancementScope.session, null, symbol, name,
visibility, modality, isExpect, isActual, isOverride,
isConst = false, isLateInit = false,
receiverTypeRef = null,
returnTypeRef = newReturnTypeRef,
isVar = isVar, initializer = null,
getter = FirDefaultPropertyGetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
setter = FirDefaultPropertySetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
delegate = null
).apply {
annotations += firField.annotations
status.isStatic = firField.isStatic
val symbol = FirPropertySymbol(original.callableId)
with(firElement) {
FirMemberPropertyImpl(
this@JavaClassEnhancementScope.session, null, symbol, name,
visibility, modality, isExpect, isActual, isOverride,
isConst = false, isLateInit = false,
receiverTypeRef = null,
returnTypeRef = newReturnTypeRef,
isVar = isVar, initializer = null,
getter = FirDefaultPropertyGetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
setter = FirDefaultPropertySetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
delegate = null
).apply {
annotations += firElement.annotations
status.isStatic = firElement.isStatic
}
}
return symbol
}
is FirJavaMethod -> {
original as FirAccessorSymbol
return enhanceMethod(
firElement, original.accessorId, original.accessorId.callableName, isAccessor = true, propertyId = original.callableId
) as FirAccessorSymbol
}
else -> {
if (original is ConePropertySymbol) return original
error("Can't make enhancement for $original: `${firElement.render()}`")
}
}
return symbol
}
private fun enhance(
@@ -107,7 +122,17 @@ class JavaClassEnhancementScope(
val firMethod = (original as FirFunctionSymbol).fir as? FirFunction
if (firMethod !is FirJavaMethod && firMethod !is FirJavaConstructor || firMethod !is FirCallableMemberDeclaration) return original
return enhanceMethod(firMethod, original.callableId, name) as FirFunctionSymbol
}
private fun enhanceMethod(
firMethod: FirFunction,
methodId: CallableId,
name: Name,
isAccessor: Boolean = false,
propertyId: CallableId? = null
): ConeCallableSymbol {
require(firMethod is FirCallableMemberDeclaration)
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firMethod.annotations)
val predefinedEnhancementInfo =
@@ -144,7 +169,7 @@ class JavaClassEnhancementScope(
)
}
val symbol = FirFunctionSymbol(original.callableId)
val symbol = if (!isAccessor) FirFunctionSymbol(methodId) else FirAccessorSymbol(callableId = propertyId!!, accessorId = methodId)
FirMemberFunctionImpl(
this@JavaClassEnhancementScope.session, null, symbol, name,
newReceiverTypeRef, newReturnTypeRef
@@ -250,8 +275,7 @@ class JavaClassEnhancementScope(
typeInSignature = TypeInSignature.ValueParameter(hasReceiver, index)
).enhance(session, jsr305State, predefinedEnhancementInfo?.parametersInfo?.getOrNull(index))
val firResolvedTypeRef = signatureParts.type
val defaultValue = ownerParameter.getDefaultValueFromAnnotation()
val defaultValueExpression = when (defaultValue) {
val defaultValueExpression = when (val defaultValue = ownerParameter.getDefaultValueFromAnnotation()) {
NullDefaultValue -> FirConstExpressionImpl(session, null, IrConstKind.Null, null)
is StringDefaultValue -> firResolvedTypeRef.type.lexicalCastFrom(session, defaultValue.value)
null -> null
@@ -8,17 +8,21 @@ package org.jetbrains.kotlin.fir.java.scopes
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractProviderBasedScope
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConeVariableSymbol
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.Name
@@ -32,7 +36,7 @@ class JavaClassUseSiteScope(
internal val symbol = klass.symbol
//base symbol as key, overridden as value
private val overriddenByBase = mutableMapOf<ConeFunctionSymbol, ConeFunctionSymbol?>()
private val overriddenByBase = mutableMapOf<ConeCallableSymbol, ConeFunctionSymbol?>()
private val context: ConeTypeContext = session.typeContext
@@ -71,14 +75,46 @@ class JavaClassUseSiteScope(
}
}
internal fun ConeFunctionSymbol.getOverridden(candidates: Set<ConeFunctionSymbol>): ConeCallableSymbol? {
private fun isOverriddenPropertyCheck(overriddenInJava: FirNamedFunction, base: FirProperty): Boolean {
val receiverTypeRef = base.receiverTypeRef
if (receiverTypeRef == null) {
// TODO: setters
return overriddenInJava.valueParameters.isEmpty()
} else {
if (overriddenInJava.valueParameters.size != 1) return false
return isEqualTypes(receiverTypeRef, overriddenInJava.valueParameters.single().returnTypeRef)
}
}
internal fun ConeCallableSymbol.getOverridden(candidates: Set<ConeFunctionSymbol>): ConeCallableSymbol? {
if (overriddenByBase.containsKey(this)) return overriddenByBase[this]
val self = (this as FirFunctionSymbol).fir as FirNamedFunction
val overriding = candidates.firstOrNull {
val member = (it as FirFunctionSymbol).firUnsafe<FirNamedFunction>()
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
} // TODO: two or more overrides for one fun?
val overriding = when (this) {
is FirFunctionSymbol -> {
val self = fir as FirNamedFunction
candidates.firstOrNull {
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
}
}
is FirPropertySymbol -> {
val self = fir as FirProperty
candidates.firstOrNull {
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
self.modality != Modality.FINAL && isOverriddenPropertyCheck(member, self)
}
}
is FirAccessorSymbol -> {
val self = fir as FirNamedFunction
candidates.firstOrNull {
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
}
}
else -> error("Unexpected callable symbol: $this")
}
// TODO: two or more overrides for one fun?
overriddenByBase[this] = overriding
return overriding
}
@@ -102,14 +138,51 @@ class JavaClassUseSiteScope(
}
}
private fun processAccessorFunctionsByName(
propertyName: Name,
accessorName: Name,
processor: (ConePropertySymbol) -> ProcessorAction
): ProcessorAction {
val overrideCandidates = mutableSetOf<ConeFunctionSymbol>()
if (!declaredMemberScope.processFunctionsByName(accessorName) { functionSymbol ->
overrideCandidates += functionSymbol
val accessorSymbol = FirAccessorSymbol(
accessorId = functionSymbol.callableId,
callableId = CallableId(functionSymbol.callableId.packageName, functionSymbol.callableId.className, propertyName)
)
if (functionSymbol is FirBasedSymbol<*>) {
(functionSymbol.fir as? FirCallableMemberDeclaration)?.let { callableMember -> accessorSymbol.bind(callableMember) }
}
processor(accessorSymbol)
}
) return ProcessorAction.STOP
return superTypesScope.processPropertiesByName(propertyName) {
val firCallableMember = (it as FirBasedSymbol<*>).fir as? FirCallableMemberDeclaration
if (firCallableMember?.isStatic == true) {
ProcessorAction.NEXT
} else {
val overriddenBy = it.getOverridden(overrideCandidates)
if (overriddenBy == null && it is ConePropertySymbol) {
processor(it)
} else {
ProcessorAction.NEXT
}
}
}
}
override fun processPropertiesByName(name: Name, processor: (ConeVariableSymbol) -> ProcessorAction): ProcessorAction {
val seen = mutableSetOf<ConeVariableSymbol>()
if (!declaredMemberScope.processPropertiesByName(name) {
seen += it
processor(it)
}
) return ProcessorAction.STOP
return ProcessorAction.NEXT
val getterName = Name.identifier(getterPrefix + name.asString().capitalize())
return processAccessorFunctionsByName(name, getterName, processor)
}
companion object {
private const val getterPrefix = "get"
}
}