org.jetbrains.kotlin.synthetic.JavaSyntheticPropertiesScope.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.synthetic
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.getRefinedUnsubstitutedMemberScopeIfPossible
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.incremental.record
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.possibleGetMethodNames
import org.jetbrains.kotlin.load.java.setMethodName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.SyntheticScope
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
import org.jetbrains.kotlin.resolve.scopes.collectSyntheticExtensionProperties
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addIfNotNull
import kotlin.properties.Delegates
fun canBePropertyAccessor(identifier: String): Boolean {
return identifier.startsWith("get") || identifier.startsWith("is") || identifier.startsWith("set")
}
interface SyntheticJavaPropertyDescriptor : PropertyDescriptor, SyntheticPropertyDescriptor {
override val getMethod: FunctionDescriptor
override val setMethod: FunctionDescriptor?
companion object {
fun findByGetterOrSetter(getterOrSetter: FunctionDescriptor, syntheticScopes: SyntheticScopes): SyntheticJavaPropertyDescriptor? {
val name = getterOrSetter.name
if (name.isSpecial) return null
val identifier = name.identifier
if (!canBePropertyAccessor(identifier)) return null // optimization
val classDescriptorOwner = getterOrSetter.containingDeclaration as? ClassDescriptor ?: return null
val originalGetterOrSetter = getterOrSetter.original
val names = propertyNamesByAccessorName(name)
return names
.flatMap {
syntheticScopes.collectSyntheticExtensionProperties(
listOf(classDescriptorOwner.defaultType),
it,
NoLookupLocation.FROM_SYNTHETIC_SCOPE
)
}.filterIsInstance()
.firstOrNull { originalGetterOrSetter == it.getMethod || originalGetterOrSetter == it.setMethod }
}
fun propertyNamesByAccessorName(name: Name): List =
org.jetbrains.kotlin.load.java.propertyNamesByAccessorName(name)
fun findByGetterOrSetter(getterOrSetter: FunctionDescriptor, syntheticScope: SyntheticScope) =
findByGetterOrSetter(getterOrSetter,
object : SyntheticScopes {
override val scopes: Collection = listOf(syntheticScope)
})
fun propertyNameByGetMethodName(methodName: Name): Name? = org.jetbrains.kotlin.load.java.propertyNameByGetMethodName(methodName)
}
}
class JavaSyntheticPropertiesScope(
storageManager: StorageManager,
private val lookupTracker: LookupTracker,
private val typeRefiner: KotlinTypeRefiner,
private val supportJavaRecords: Boolean,
) : SyntheticScope.Default() {
private val syntheticPropertyInClass =
storageManager.createMemoizedFunction, SyntheticPropertyHolder> { pair ->
syntheticPropertyInClassNotCached(pair.first, pair.second)
}
private fun getSyntheticPropertyAndRecordLookups(
classifier: ClassDescriptor,
name: Name,
location: LookupLocation
): PropertyDescriptor? {
val (descriptor, lookedNames) = syntheticPropertyInClass(Pair(classifier, name))
if (location !is NoLookupLocation) {
lookedNames.forEach { lookupTracker.record(location, classifier, it) }
}
return descriptor
}
private fun syntheticPropertyInClassNotCached(ownerClass: ClassDescriptor, name: Name): SyntheticPropertyHolder {
val forBean = syntheticPropertyHolderForBeanConvention(name, ownerClass)
if (forBean.descriptor != null) return forBean
if (!ownerClass.isRecord()) return forBean
val propertyForComponent = syntheticPropertyDescriptorForRecordComponent(name, ownerClass)
return createSyntheticPropertyHolder(propertyForComponent, forBean.lookedNames, name)
}
private fun createSyntheticPropertyHolder(
descriptor: PropertyDescriptor?,
lookedNames: List,
additionalName: Name? = null
): SyntheticPropertyHolder {
if (lookupTracker === LookupTracker.DO_NOTHING) {
return if (descriptor == null) SyntheticPropertyHolder.EMPTY else SyntheticPropertyHolder(descriptor, emptyList())
}
val names = ArrayList(lookedNames.size + (additionalName?.let { 1 } ?: 0))
names.addAll(lookedNames)
names.addIfNotNull(additionalName)
return SyntheticPropertyHolder(descriptor, names)
}
private fun syntheticPropertyHolderForBeanConvention(
name: Name,
ownerClass: ClassDescriptor
): SyntheticPropertyHolder {
val possibleGetMethodNames = possibleGetMethodNames(name)
if (possibleGetMethodNames.isEmpty()) return SyntheticPropertyHolder.EMPTY
val memberScope = ownerClass.getRefinedUnsubstitutedMemberScopeIfPossible(typeRefiner)
val getMethod = possibleGetMethodNames
.flatMap { memberScope.getContributedFunctions(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE) }
.singleOrNull {
it.hasJavaOriginInHierarchy() && isGoodGetMethod(it)
} ?: return createSyntheticPropertyHolder(null, possibleGetMethodNames)
val setMethodName = setMethodName(getMethod.name)
val setMethod = memberScope.getContributedFunctions(setMethodName, NoLookupLocation.FROM_SYNTHETIC_SCOPE)
.singleOrNull { isGoodSetMethod(it, getMethod) }
val propertyType = getMethod.returnType!!
val descriptor = MyPropertyDescriptor.create(ownerClass, getMethod.original, setMethod?.original, name, propertyType)
return createSyntheticPropertyHolder(descriptor, possibleGetMethodNames, setMethodName)
}
private fun syntheticPropertyDescriptorForRecordComponent(
name: Name,
ownerClass: ClassDescriptor
): PropertyDescriptor? {
if (!supportJavaRecords) return null
val componentLikeMethod =
ownerClass.getRefinedUnsubstitutedMemberScopeIfPossible(typeRefiner)
.getContributedFunctions(name, NoLookupLocation.FROM_SYNTHETIC_SCOPE)
.singleOrNull(this::isGoodGetMethod) ?: return null
if (componentLikeMethod !is JavaMethodDescriptor || !componentLikeMethod.isForRecordComponent) {
return null
}
val propertyType = componentLikeMethod.returnType!!
return MyPropertyDescriptor.create(ownerClass, componentLikeMethod.original, null, name, propertyType)
}
private fun isGoodGetMethod(descriptor: FunctionDescriptor): Boolean {
val returnType = descriptor.returnType ?: return false
if (returnType.isUnit()) return false
return descriptor.valueParameters.isEmpty()
&& descriptor.typeParameters.isEmpty()
&& descriptor.visibility.isVisibleOutside()
&& !(descriptor.isHiddenForResolutionEverywhereBesideSupercalls && descriptor.name.asString() == "isEmpty") // CharSequence.isEmpty() from JDK15
}
private fun isGoodSetMethod(descriptor: FunctionDescriptor, getMethod: FunctionDescriptor): Boolean {
val propertyType = getMethod.returnType ?: return false
val parameter = descriptor.valueParameters.singleOrNull() ?: return false
if (!TypeUtils.equalTypes(parameter.type, propertyType)) {
if (!propertyType.isSubtypeOf(parameter.type)) return false
if (descriptor.findOverridden {
val baseProperty = SyntheticJavaPropertyDescriptor.findByGetterOrSetter(it, this)
baseProperty?.getMethod?.name == getMethod.name
} == null) return false
}
return parameter.varargElementType == null
&& descriptor.typeParameters.isEmpty()
&& descriptor.visibility.isVisibleOutside()
&& !(descriptor.isHiddenForResolutionEverywhereBesideSupercalls && descriptor.name.asString() == "isEmpty") // CharSequence.isEmpty() from JDK15
}
private fun FunctionDescriptor.findOverridden(condition: (FunctionDescriptor) -> Boolean): FunctionDescriptor? {
for (descriptor in overriddenDescriptors) {
if (condition(descriptor)) return descriptor
descriptor.findOverridden(condition)?.let { return it }
}
return null
}
override fun getSyntheticExtensionProperties(
receiverTypes: Collection,
name: Name,
location: LookupLocation
): Collection {
var result: SmartList? = null
val processedTypes: MutableSet? = if (receiverTypes.size > 1) HashSet() else null
for (type in receiverTypes) {
result = collectSyntheticPropertiesByName(result, type.constructor, name, processedTypes, location)
}
return when {
result == null -> emptyList()
result.size > 1 -> result.toSet()
else -> result
}
}
private fun collectSyntheticPropertiesByName(
result: SmartList?,
type: TypeConstructor,
name: Name,
processedTypes: MutableSet?,
location: LookupLocation
): SmartList? {
if (processedTypes != null && !processedTypes.add(type)) return result
@Suppress("NAME_SHADOWING")
var result = result
val classifier = type.declarationDescriptor
if (classifier is ClassDescriptor) {
result = result.add(getSyntheticPropertyAndRecordLookups(classifier, name, location))
} else {
type.supertypes.forEach { result = collectSyntheticPropertiesByName(result, it.constructor, name, processedTypes, location) }
}
return result
}
override fun getSyntheticExtensionProperties(
receiverTypes: Collection,
location: LookupLocation
): Collection {
val result = ArrayList()
val processedTypes = HashSet()
receiverTypes.forEach { result.collectSyntheticProperties(it.constructor, processedTypes) }
return result
}
private fun MutableList.collectSyntheticProperties(
type: TypeConstructor,
processedTypes: MutableSet
) {
if (!processedTypes.add(type)) return
val classifier = type.declarationDescriptor
if (classifier is ClassDescriptor) {
val unsubstitutedMemberScope = classifier.getRefinedUnsubstitutedMemberScopeIfPossible(typeRefiner)
for (descriptor in unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)) {
if (descriptor is FunctionDescriptor) {
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(descriptor.getName())
if (propertyName != null) {
addIfNotNull(syntheticPropertyInClass(Pair(classifier, propertyName)).descriptor)
}
if (classifier.isRecord()) {
addIfNotNull(syntheticPropertyInClass(Pair(classifier, descriptor.name)).descriptor)
}
}
}
} else {
type.supertypes.forEach { collectSyntheticProperties(it.constructor, processedTypes) }
}
}
private fun ClassifierDescriptor.isRecord() =
this is JavaClassDescriptor && isRecord
private fun SmartList?.add(property: PropertyDescriptor?): SmartList? {
if (property == null) return this
val list = if (this != null) this else SmartList()
list.add(property)
return list
}
override fun getSyntheticMemberFunctions(
receiverTypes: Collection,
name: Name,
location: LookupLocation
): Collection = emptyList()
override fun getSyntheticMemberFunctions(receiverTypes: Collection): Collection = emptyList()
private data class SyntheticPropertyHolder(val descriptor: PropertyDescriptor?, val lookedNames: List) {
companion object {
val EMPTY = SyntheticPropertyHolder(null, emptyList())
}
}
private class MyPropertyDescriptor(
containingDeclaration: DeclarationDescriptor,
original: PropertyDescriptor?,
annotations: Annotations,
modality: Modality,
visibility: DescriptorVisibility,
isVar: Boolean,
name: Name,
kind: CallableMemberDescriptor.Kind,
source: SourceElement
) : SyntheticJavaPropertyDescriptor, PropertyDescriptorImpl(
containingDeclaration, original, annotations, modality, visibility, isVar, name, kind, source,
/* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false,
/* isDelegated = */ false
) {
override var getMethod: FunctionDescriptor by Delegates.notNull()
private set
override var setMethod: FunctionDescriptor? = null
private set
companion object {
fun create(
ownerClass: ClassDescriptor,
getMethod: FunctionDescriptor,
setMethod: FunctionDescriptor?,
name: Name,
type: KotlinType
): MyPropertyDescriptor {
val visibility = syntheticVisibility(getMethod, isUsedForExtension = true)
val descriptor = MyPropertyDescriptor(
DescriptorUtils.getContainingModule(ownerClass),
null,
Annotations.EMPTY,
Modality.FINAL,
visibility,
setMethod != null,
name,
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE
)
descriptor.getMethod = getMethod
descriptor.setMethod = setMethod
val classTypeParams = ownerClass.typeConstructor.parameters
val typeParameters = ArrayList(classTypeParams.size)
val typeSubstitutor =
DescriptorSubstitutor.substituteTypeParameters(classTypeParams, TypeSubstitution.EMPTY, descriptor, typeParameters)
val propertyType = typeSubstitutor.safeSubstitute(type, Variance.INVARIANT)
val receiverType = typeSubstitutor.safeSubstitute(ownerClass.defaultType, Variance.INVARIANT)
descriptor.setType(
propertyType, typeParameters, null,
DescriptorFactory.createExtensionReceiverParameterForCallable(descriptor, receiverType, Annotations.EMPTY), emptyList()
)
val getter = PropertyGetterDescriptorImpl(
descriptor,
getMethod.annotations,
Modality.FINAL,
visibility,
false,
getMethod.isExternal,
false,
CallableMemberDescriptor.Kind.SYNTHESIZED,
null,
SourceElement.NO_SOURCE
)
getter.initialize(null)
val setter = if (setMethod != null)
PropertySetterDescriptorImpl(
descriptor,
setMethod.annotations,
Modality.FINAL,
syntheticVisibility(setMethod, isUsedForExtension = true),
false,
setMethod.isExternal,
false,
CallableMemberDescriptor.Kind.SYNTHESIZED,
null,
SourceElement.NO_SOURCE
)
else
null
setter?.initializeDefault()
descriptor.initialize(getter, setter)
return descriptor
}
}
override fun createSubstitutedCopy(
newOwner: DeclarationDescriptor,
newModality: Modality,
newVisibility: DescriptorVisibility,
original: PropertyDescriptor?,
kind: CallableMemberDescriptor.Kind,
newName: Name,
source: SourceElement
): PropertyDescriptorImpl {
return MyPropertyDescriptor(newOwner, this, annotations, newModality, newVisibility, isVar, newName, kind, this.source).apply {
getMethod = [email protected]
setMethod = [email protected]
}
}
override fun substitute(substitutor: TypeSubstitutor): PropertyDescriptor? {
val descriptor = super.substitute(substitutor) as MyPropertyDescriptor? ?: return null
if (descriptor == this) return descriptor
val classTypeParameters = (getMethod.containingDeclaration as ClassDescriptor).typeConstructor.parameters
val substitutionMap = HashMap()
for ((typeParameter, classTypeParameter) in typeParameters.zip(classTypeParameters)) {
val typeProjection = substitutor.substitution[typeParameter.defaultType] ?: continue
substitutionMap[classTypeParameter.typeConstructor] = typeProjection
}
val classParametersSubstitutor = TypeConstructorSubstitution.createByConstructorsMap(
substitutionMap,
approximateCapturedTypes = true
).buildSubstitutor()
descriptor.getMethod = getMethod.substitute(classParametersSubstitutor) ?: return null
descriptor.setMethod = setMethod?.substitute(classParametersSubstitutor)
return descriptor
}
}
}