Fix UL classes descriptors leaks
UL classes built upon descriptors and UL support classes are saving a descriptors. This could lead to descriptors leakage so we have to eliminate any fields with descriptor references. Fixed #KT-34337
This commit is contained in:
+1
-26
@@ -11,46 +11,21 @@ import com.intellij.psi.impl.PsiImplUtil
|
||||
import com.intellij.psi.impl.light.LightIdentifier
|
||||
import com.intellij.psi.meta.PsiMetaData
|
||||
import com.intellij.psi.util.TypeConversionUtil
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
|
||||
import org.jetbrains.kotlin.asJava.elements.psiType
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtCallElement
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.nullability
|
||||
|
||||
class KtUltraLightNullabilityAnnotation(
|
||||
member: KtUltraLightElementWithNullabilityAnnotation<*, *>,
|
||||
parent: PsiElement
|
||||
) : KtLightNullabilityAnnotation<KtUltraLightElementWithNullabilityAnnotation<*, *>>(member, parent) {
|
||||
override fun getQualifiedName(): String? {
|
||||
val kotlinType = member.kotlinTypeForNullabilityAnnotation?.takeUnless(KotlinType::isError) ?: return null
|
||||
val psiType = member.psiTypeForNullabilityAnnotation ?: return null
|
||||
if (psiType is PsiPrimitiveType) return null
|
||||
|
||||
if (kotlinType.isTypeParameter()) {
|
||||
if (!TypeUtils.hasNullableSuperType(kotlinType)) return NotNull::class.java.name
|
||||
if (!kotlinType.isMarkedNullable) return null
|
||||
}
|
||||
|
||||
val nullability = kotlinType.nullability()
|
||||
return when (nullability) {
|
||||
TypeNullability.NOT_NULL -> NotNull::class.java.name
|
||||
TypeNullability.NULLABLE -> Nullable::class.java.name
|
||||
TypeNullability.FLEXIBLE -> null
|
||||
}
|
||||
}
|
||||
override fun getQualifiedName(): String? = member.qualifiedNameForNullabilityAnnotation
|
||||
}
|
||||
|
||||
fun AnnotationDescriptor.toLightAnnotation(ultraLightSupport: KtUltraLightSupport, parent: PsiElement) =
|
||||
|
||||
@@ -160,7 +160,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
|
||||
return forExtendsList == !JvmCodegenUtil.isJvmInterface(supertype)
|
||||
}
|
||||
|
||||
override fun buildTypeParameterList(): PsiTypeParameterList = buildTypeParameterList(classOrObject, this, support)
|
||||
override fun buildTypeParameterList(): PsiTypeParameterList = buildTypeParameterListForSourceDeclaration(classOrObject, this, support)
|
||||
|
||||
// the following logic should be in the platform (super), overrides can be removed once that happens
|
||||
override fun getInterfaces(): Array<PsiClass> = PsiClassImplUtil.getInterfaces(this)
|
||||
|
||||
+27
-1
@@ -6,13 +6,39 @@
|
||||
package org.jetbrains.kotlin.asJava.classes
|
||||
|
||||
import com.intellij.psi.PsiModifierListOwner
|
||||
import com.intellij.psi.PsiPrimitiveType
|
||||
import com.intellij.psi.PsiType
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.nullability
|
||||
|
||||
interface KtUltraLightElementWithNullabilityAnnotation<out T : KtDeclaration, out D : PsiModifierListOwner> : KtLightDeclaration<T, D>,
|
||||
PsiModifierListOwner {
|
||||
val kotlinTypeForNullabilityAnnotation: KotlinType?
|
||||
|
||||
fun computeQualifiedNameForNullabilityAnnotation(kotlinType: KotlinType?): String? {
|
||||
val notErrorKotlinType = kotlinType?.takeUnless(KotlinType::isError) ?: return null
|
||||
val psiType = psiTypeForNullabilityAnnotation ?: return null
|
||||
if (psiType is PsiPrimitiveType) return null
|
||||
|
||||
if (notErrorKotlinType.isTypeParameter()) {
|
||||
if (!TypeUtils.hasNullableSuperType(notErrorKotlinType)) return NotNull::class.java.name
|
||||
if (!notErrorKotlinType.isMarkedNullable) return null
|
||||
}
|
||||
|
||||
return when (notErrorKotlinType.nullability()) {
|
||||
TypeNullability.NOT_NULL -> NotNull::class.java.name
|
||||
TypeNullability.NULLABLE -> Nullable::class.java.name
|
||||
TypeNullability.FLEXIBLE -> null
|
||||
}
|
||||
}
|
||||
|
||||
val qualifiedNameForNullabilityAnnotation: String?
|
||||
val psiTypeForNullabilityAnnotation: PsiType?
|
||||
}
|
||||
@@ -82,10 +82,10 @@ internal open class KtUltraLightFieldImpl protected constructor(
|
||||
|
||||
override fun getLanguage(): Language = KotlinLanguage.INSTANCE
|
||||
|
||||
private val propertyDescriptor: PropertyDescriptor? by lazyPub { declaration.resolve() as? PropertyDescriptor }
|
||||
private val propertyDescriptor: PropertyDescriptor? get() = declaration.resolve() as? PropertyDescriptor
|
||||
|
||||
private val kotlinType: KotlinType? by lazyPub {
|
||||
when {
|
||||
private val kotlinType: KotlinType?
|
||||
get() = when {
|
||||
declaration is KtProperty && declaration.hasDelegate() ->
|
||||
propertyDescriptor?.let {
|
||||
val context = LightClassGenerationSupport.getInstance(project).analyze(declaration)
|
||||
@@ -100,11 +100,13 @@ internal open class KtUltraLightFieldImpl protected constructor(
|
||||
declaration.getKotlinType()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val kotlinTypeForNullabilityAnnotation: KotlinType?
|
||||
// We don't generate nullability annotations for non-backing fields in backend
|
||||
get() = kotlinType?.takeUnless { declaration is KtEnumEntry || declaration is KtObjectDeclaration }
|
||||
override val qualifiedNameForNullabilityAnnotation: String?
|
||||
get() {
|
||||
// We don't generate nullability annotations for non-backing fields in backend
|
||||
val typeForAnnotation = kotlinType?.takeUnless { declaration is KtEnumEntry || declaration is KtObjectDeclaration }
|
||||
return computeQualifiedNameForNullabilityAnnotation(typeForAnnotation)
|
||||
}
|
||||
|
||||
override val psiTypeForNullabilityAnnotation: PsiType?
|
||||
get() = type
|
||||
|
||||
+101
-39
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.asJava.builder.MemberIndex
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl
|
||||
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
@@ -25,7 +26,6 @@ import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
internal abstract class KtUltraLightMethod(
|
||||
internal val delegate: PsiMethod,
|
||||
@@ -38,6 +38,37 @@ internal abstract class KtUltraLightMethod(
|
||||
containingClass
|
||||
), KtUltraLightElementWithNullabilityAnnotation<KtDeclaration, PsiMethod> {
|
||||
|
||||
private class KtUltraLightThrowsReferenceListBuilder(private val parentMethod: PsiMethod) :
|
||||
KotlinLightReferenceListBuilder(parentMethod.manager, parentMethod.language, PsiReferenceList.Role.THROWS_LIST) {
|
||||
override fun getParent() = parentMethod
|
||||
override fun getContainingFile() = parentMethod.containingFile
|
||||
}
|
||||
|
||||
protected fun computeThrowsList(methodDescriptor: FunctionDescriptor?): PsiReferenceList {
|
||||
val builder = KtUltraLightThrowsReferenceListBuilder(parentMethod = this)
|
||||
|
||||
if (methodDescriptor !== null) {
|
||||
for (ex in FunctionCodegen.getThrownExceptions(methodDescriptor)) {
|
||||
val psiClassType = ex.defaultType.asPsiType(support, TypeMappingMode.DEFAULT, builder) as? PsiClassType
|
||||
psiClassType ?: continue
|
||||
builder.addReference(psiClassType)
|
||||
}
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
protected fun computeCheckNeedToErasureParametersTypes(methodDescriptor: FunctionDescriptor?): Boolean {
|
||||
return methodDescriptor
|
||||
?.getSpecialSignatureInfo()
|
||||
?.let { it.valueParametersSignature !== null }
|
||||
?: false
|
||||
}
|
||||
|
||||
abstract override fun buildTypeParameterList(): PsiTypeParameterList
|
||||
|
||||
abstract val checkNeedToErasureParametersTypes: Boolean
|
||||
|
||||
override val memberIndex: MemberIndex? = null
|
||||
|
||||
override val psiTypeForNullabilityAnnotation: PsiType?
|
||||
@@ -54,23 +85,6 @@ internal abstract class KtUltraLightMethod(
|
||||
// should be in super
|
||||
override fun isVarArgs() = PsiImplUtil.isVarArgs(this)
|
||||
|
||||
abstract override fun buildTypeParameterList(): PsiTypeParameterList
|
||||
|
||||
private val _throwsList: PsiReferenceList by lazyPub {
|
||||
val list =
|
||||
object : KotlinLightReferenceListBuilder(manager, language, PsiReferenceList.Role.THROWS_LIST) {
|
||||
override fun getParent() = this@KtUltraLightMethod
|
||||
override fun getContainingFile() = this@KtUltraLightMethod.containingFile
|
||||
}
|
||||
methodDescriptor?.let {
|
||||
for (ex in FunctionCodegen.getThrownExceptions(it)) {
|
||||
val psiClassType = ex.defaultType.asPsiType(support, TypeMappingMode.DEFAULT, list) as? PsiClassType ?: continue
|
||||
list.addReference(psiClassType)
|
||||
}
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
private val _deprecated: Boolean by lazyPub { kotlinOrigin?.isDeprecated(support) ?: false }
|
||||
|
||||
override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this)
|
||||
@@ -90,17 +104,6 @@ internal abstract class KtUltraLightMethod(
|
||||
override fun findSuperMethods(parentClass: PsiClass?): Array<out PsiMethod> =
|
||||
PsiSuperMethodImplUtil.findSuperMethods(this, parentClass)
|
||||
|
||||
override fun getThrowsList(): PsiReferenceList = _throwsList
|
||||
|
||||
abstract val methodDescriptor: FunctionDescriptor?
|
||||
|
||||
val checkNeedToErasureParametersTypes: Boolean by lazyPub {
|
||||
methodDescriptor
|
||||
?.getSpecialSignatureInfo()
|
||||
?.let { it.valueParametersSignature !== null }
|
||||
?: false
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean = this === other
|
||||
|
||||
override fun hashCode(): Int = name.hashCode()
|
||||
@@ -127,21 +130,29 @@ internal class KtUltraLightMethodForSourceDeclaration(
|
||||
containingClass: KtLightClass
|
||||
) : this(delegate, LightMemberOriginForDeclaration(declaration, JvmDeclarationOriginKind.OTHER), support, containingClass)
|
||||
|
||||
override val kotlinTypeForNullabilityAnnotation: KotlinType?
|
||||
get() = if (forceToSkipNullabilityAnnotation) null else kotlinOrigin?.getKotlinType()
|
||||
override val qualifiedNameForNullabilityAnnotation: String?
|
||||
get() {
|
||||
val typeForAnnotation = if (forceToSkipNullabilityAnnotation) null else kotlinOrigin?.getKotlinType()
|
||||
return computeQualifiedNameForNullabilityAnnotation(typeForAnnotation)
|
||||
}
|
||||
|
||||
override fun buildTypeParameterList(): PsiTypeParameterList {
|
||||
val origin = kotlinOrigin
|
||||
return if (origin is KtFunction || origin is KtProperty)
|
||||
buildTypeParameterList(origin as KtTypeParameterListOwner, this, support)
|
||||
buildTypeParameterListForSourceDeclaration(origin as KtTypeParameterListOwner, this, support)
|
||||
else LightTypeParameterListBuilder(manager, language)
|
||||
}
|
||||
|
||||
override val methodDescriptor = kotlinOrigin?.resolve() as? FunctionDescriptor
|
||||
private val methodDescriptor get() = kotlinOrigin?.resolve() as? FunctionDescriptor
|
||||
|
||||
private val _throwsList: PsiReferenceList by lazyPub { computeThrowsList(methodDescriptor) }
|
||||
override fun getThrowsList(): PsiReferenceList = _throwsList
|
||||
|
||||
override val checkNeedToErasureParametersTypes: Boolean by lazyPub { computeCheckNeedToErasureParametersTypes(methodDescriptor) }
|
||||
}
|
||||
|
||||
internal class KtUltraLightMethodForDescriptor(
|
||||
override val methodDescriptor: FunctionDescriptor,
|
||||
methodDescriptor: FunctionDescriptor,
|
||||
delegate: LightMethodBuilder,
|
||||
lightMemberOrigin: LightMemberOrigin?,
|
||||
support: KtUltraLightSupport,
|
||||
@@ -152,11 +163,62 @@ internal class KtUltraLightMethodForDescriptor(
|
||||
support,
|
||||
containingClass
|
||||
) {
|
||||
override fun buildTypeParameterList() = buildTypeParameterList(methodDescriptor, this, support)
|
||||
// This is greedy realization of UL class.
|
||||
// This means that all data that depends on descriptor evaluated in ctor so the descriptor will be released on the end.
|
||||
// Be aware to save descriptor in class instance or any depending references
|
||||
|
||||
override val kotlinTypeForNullabilityAnnotation: KotlinType?
|
||||
get() = methodDescriptor.returnType
|
||||
private val lazyInitializers = mutableListOf<Lazy<*>>()
|
||||
private inline fun <T> getAndAddLazy(crossinline initializer: () -> T): Lazy<T> =
|
||||
lazyPub { initializer() }.also { lazyInitializers.add(it) }
|
||||
|
||||
override val givenAnnotations: List<KtLightAbstractAnnotation>
|
||||
get() = methodDescriptor.obtainLightAnnotations(support, this)
|
||||
|
||||
private val _buildTypeParameterList by getAndAddLazy {
|
||||
buildTypeParameterListForDescriptor(methodDescriptor, this, support)
|
||||
}
|
||||
|
||||
override fun buildTypeParameterList() = _buildTypeParameterList
|
||||
|
||||
private val _throwsList: PsiReferenceList by getAndAddLazy {
|
||||
computeThrowsList(methodDescriptor)
|
||||
}
|
||||
|
||||
override fun getThrowsList(): PsiReferenceList = _throwsList
|
||||
|
||||
override val givenAnnotations: List<KtLightAbstractAnnotation> by getAndAddLazy {
|
||||
methodDescriptor.obtainLightAnnotations(support, this)
|
||||
}
|
||||
|
||||
override val qualifiedNameForNullabilityAnnotation: String? by getAndAddLazy {
|
||||
computeQualifiedNameForNullabilityAnnotation(methodDescriptor.returnType)
|
||||
}
|
||||
|
||||
override val checkNeedToErasureParametersTypes: Boolean by getAndAddLazy {
|
||||
computeCheckNeedToErasureParametersTypes(methodDescriptor)
|
||||
}
|
||||
|
||||
init {
|
||||
methodDescriptor.extensionReceiverParameter?.let { receiver ->
|
||||
delegate.addParameter(KtUltraLightParameterForDescriptor(receiver, support, this))
|
||||
}
|
||||
|
||||
for (valueParameter in methodDescriptor.valueParameters) {
|
||||
delegate.addParameter(KtUltraLightParameterForDescriptor(valueParameter, support, this))
|
||||
}
|
||||
|
||||
val returnType = if (methodDescriptor is ConstructorDescriptor) {
|
||||
delegate.isConstructor = true
|
||||
PsiType.VOID
|
||||
} else {
|
||||
support.mapType(this) { typeMapper, signatureWriter ->
|
||||
typeMapper.mapReturnType(methodDescriptor, signatureWriter)
|
||||
}
|
||||
}
|
||||
delegate.setMethodReturnType(returnType)
|
||||
|
||||
//We should force computations on all lazy delegates to release descriptor on the end of ctor call
|
||||
with(lazyInitializers) {
|
||||
forEach { it.value }
|
||||
clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
-43
@@ -27,16 +27,19 @@ internal class KtUltraLightSuspendContinuationParameter(
|
||||
KtLightParameter,
|
||||
KtUltraLightElementWithNullabilityAnnotation<KtParameter, PsiParameter> {
|
||||
|
||||
override val kotlinTypeForNullabilityAnnotation: KotlinType? get() = ktType
|
||||
override val qualifiedNameForNullabilityAnnotation: String?
|
||||
get() = computeQualifiedNameForNullabilityAnnotation(ktType)
|
||||
|
||||
override val psiTypeForNullabilityAnnotation: PsiType? get() = psiType
|
||||
override val kotlinOrigin: KtParameter? = null
|
||||
override val clsDelegate: PsiParameter get() = invalidAccess()
|
||||
|
||||
private val ktType by lazyPub {
|
||||
val descriptor = ktFunction.resolve() as? FunctionDescriptor
|
||||
val returnType = descriptor?.returnType ?: return@lazyPub null
|
||||
support.moduleDescriptor.getContinuationOfTypeOrAny(returnType, support.isReleasedCoroutine)
|
||||
}
|
||||
private val ktType: KotlinType?
|
||||
get() {
|
||||
val descriptor = ktFunction.resolve() as? FunctionDescriptor
|
||||
val returnType = descriptor?.returnType ?: return null
|
||||
return support.moduleDescriptor.getContinuationOfTypeOrAny(returnType, support.isReleasedCoroutine)
|
||||
}
|
||||
|
||||
private val psiType by lazyPub {
|
||||
ktType?.asPsiType(support, TypeMappingMode.DEFAULT, method) ?: PsiType.NULL
|
||||
@@ -68,12 +71,12 @@ internal abstract class KtUltraLightParameter(
|
||||
name: String,
|
||||
override val kotlinOrigin: KtParameter?,
|
||||
protected val support: KtUltraLightSupport,
|
||||
method: KtUltraLightMethod
|
||||
private val ultraLightMethod: KtUltraLightMethod
|
||||
) : org.jetbrains.kotlin.asJava.elements.LightParameter(
|
||||
name,
|
||||
PsiType.NULL,
|
||||
method,
|
||||
method.language
|
||||
ultraLightMethod,
|
||||
ultraLightMethod.language
|
||||
), KtUltraLightElementWithNullabilityAnnotation<KtParameter, PsiParameter>, KtLightParameter {
|
||||
|
||||
override fun isEquivalentTo(another: PsiElement?): Boolean = kotlinOrigin == another
|
||||
@@ -89,41 +92,31 @@ internal abstract class KtUltraLightParameter(
|
||||
|
||||
override fun isValid() = parent.isValid
|
||||
|
||||
protected abstract val kotlinType: KotlinType?
|
||||
protected abstract val containingDescriptor: CallableDescriptor?
|
||||
|
||||
override val kotlinTypeForNullabilityAnnotation: KotlinType?
|
||||
get() {
|
||||
val type = kotlinType
|
||||
return if (isVarArgs && type != null && KotlinBuiltIns.isArray(type)) {
|
||||
type.arguments[0].type
|
||||
} else {
|
||||
type
|
||||
}
|
||||
}
|
||||
override fun computeQualifiedNameForNullabilityAnnotation(kotlinType: KotlinType?): String? {
|
||||
val typeForAnnotation =
|
||||
if (isVarArgs && kotlinType != null && KotlinBuiltIns.isArray(kotlinType)) kotlinType.arguments[0].type else kotlinType
|
||||
return super.computeQualifiedNameForNullabilityAnnotation(typeForAnnotation)
|
||||
}
|
||||
|
||||
override val psiTypeForNullabilityAnnotation: PsiType?
|
||||
get() = type
|
||||
|
||||
|
||||
private val _type: PsiType by lazyPub {
|
||||
val kotlinType = kotlinType ?: return@lazyPub PsiType.NULL
|
||||
protected fun computeParameterType(kotlinType: KotlinType?, containingDeclaration: CallableDescriptor?): PsiType {
|
||||
kotlinType ?: return PsiType.NULL
|
||||
|
||||
if (kotlinType.isSuspendFunctionType) {
|
||||
kotlinType.asPsiType(support, TypeMappingMode.DEFAULT, this)
|
||||
return kotlinType.asPsiType(support, TypeMappingMode.DEFAULT, this)
|
||||
} else {
|
||||
val containingDescriptor = containingDescriptor ?: return@lazyPub PsiType.NULL
|
||||
val containingDescriptor = containingDeclaration ?: return PsiType.NULL
|
||||
val mappedType = support.mapType(this) { typeMapper, sw ->
|
||||
typeMapper.writeParameterType(sw, kotlinType, containingDescriptor)
|
||||
}
|
||||
|
||||
if (method.checkNeedToErasureParametersTypes)
|
||||
TypeConversionUtil.erasure(mappedType)
|
||||
else mappedType
|
||||
return if (ultraLightMethod.checkNeedToErasureParametersTypes) TypeConversionUtil.erasure(mappedType) else mappedType
|
||||
}
|
||||
}
|
||||
|
||||
override fun getType(): PsiType = _type
|
||||
abstract override fun getType(): PsiType
|
||||
|
||||
override fun getContainingFile(): PsiFile = method.containingFile
|
||||
override fun getParent(): PsiElement = method.parameterList
|
||||
@@ -143,7 +136,21 @@ internal abstract class KtAbstractUltraLightParameterForDeclaration(
|
||||
method: KtUltraLightMethod,
|
||||
protected val containingDeclaration: KtCallableDeclaration
|
||||
) : KtUltraLightParameter(name, kotlinOrigin, support, method) {
|
||||
override val containingDescriptor: CallableDescriptor? = containingDeclaration.resolve() as? CallableMemberDescriptor
|
||||
|
||||
protected fun tryGetContainingDescriptor(): CallableDescriptor? =
|
||||
containingDeclaration.resolve() as? CallableMemberDescriptor
|
||||
|
||||
protected abstract fun tryGetKotlinType(): KotlinType?
|
||||
|
||||
private val _parameterType: PsiType by lazyPub {
|
||||
computeParameterType(tryGetKotlinType(), tryGetContainingDescriptor())
|
||||
}
|
||||
|
||||
override fun getType(): PsiType = _parameterType
|
||||
|
||||
override val qualifiedNameForNullabilityAnnotation: String? by lazyPub {
|
||||
computeQualifiedNameForNullabilityAnnotation(tryGetKotlinType())
|
||||
}
|
||||
}
|
||||
|
||||
internal class KtUltraLightParameterForSource(
|
||||
@@ -154,9 +161,7 @@ internal class KtUltraLightParameterForSource(
|
||||
containingDeclaration: KtCallableDeclaration
|
||||
) : KtAbstractUltraLightParameterForDeclaration(name, kotlinOrigin, support, method, containingDeclaration) {
|
||||
|
||||
override val kotlinType: KotlinType? by lazyPub {
|
||||
kotlinOrigin.getKotlinType()
|
||||
}
|
||||
override fun tryGetKotlinType(): KotlinType? = kotlinOrigin.getKotlinType()
|
||||
|
||||
override fun isVarArgs(): Boolean = kotlinOrigin.isVarArg && method.parameterList.parameters.last() == this
|
||||
|
||||
@@ -175,7 +180,7 @@ internal class KtUltraLightParameterForSetterParameter(
|
||||
containingDeclaration: KtCallableDeclaration
|
||||
) : KtAbstractUltraLightParameterForDeclaration(name, null, support, method, containingDeclaration) {
|
||||
|
||||
override val kotlinType: KotlinType? by lazyPub { property.getKotlinType() }
|
||||
override fun tryGetKotlinType(): KotlinType? = property.getKotlinType()
|
||||
|
||||
override fun isVarArgs(): Boolean = false
|
||||
}
|
||||
@@ -188,24 +193,51 @@ internal class KtUltraLightReceiverParameter(
|
||||
|
||||
override fun isVarArgs(): Boolean = false
|
||||
|
||||
override val kotlinType: KotlinType? by lazyPub { containingDescriptor?.extensionReceiverParameter?.type }
|
||||
override fun tryGetKotlinType(): KotlinType? =
|
||||
tryGetContainingDescriptor()?.extensionReceiverParameter?.type
|
||||
}
|
||||
|
||||
internal class KtUltraLightParameterForDescriptor(
|
||||
private val descriptor: ParameterDescriptor,
|
||||
descriptor: ParameterDescriptor,
|
||||
support: KtUltraLightSupport,
|
||||
method: KtUltraLightMethod
|
||||
) : KtUltraLightParameter(
|
||||
if (descriptor.name.isSpecial) "\$self" else descriptor.name.identifier,
|
||||
null, support, method
|
||||
) {
|
||||
override val kotlinType: KotlinType?
|
||||
get() = descriptor.type
|
||||
// This is greedy realization of UL class.
|
||||
// This means that all data that depends on descriptor evaluated in ctor so the descriptor will be released on the end.
|
||||
// Be aware to save descriptor in class instance or any depending references
|
||||
|
||||
override val containingDescriptor: CallableDescriptor? = descriptor.containingDeclaration as? CallableMemberDescriptor
|
||||
private val lazyInitializers = mutableListOf<Lazy<*>>()
|
||||
private inline fun <T> getAndAddLazy(crossinline initializer: () -> T): Lazy<T> =
|
||||
lazyPub { initializer() }.also { lazyInitializers.add(it) }
|
||||
|
||||
override fun isVarArgs() = (descriptor as? ValueParameterDescriptor)?.varargElementType != null
|
||||
override val qualifiedNameForNullabilityAnnotation: String? by getAndAddLazy {
|
||||
computeQualifiedNameForNullabilityAnnotation(descriptor.type)
|
||||
}
|
||||
|
||||
override val givenAnnotations: List<KtLightAbstractAnnotation>
|
||||
get() = descriptor.obtainLightAnnotations(support, this)
|
||||
private val _isVarArgs: Boolean by getAndAddLazy {
|
||||
(descriptor as? ValueParameterDescriptor)?.varargElementType != null
|
||||
}
|
||||
|
||||
override fun isVarArgs() = _isVarArgs
|
||||
|
||||
override val givenAnnotations: List<KtLightAbstractAnnotation> by getAndAddLazy {
|
||||
descriptor.obtainLightAnnotations(support, this)
|
||||
}
|
||||
|
||||
private val _parameterType by getAndAddLazy {
|
||||
computeParameterType(descriptor.type, descriptor.containingDeclaration as? CallableMemberDescriptor)
|
||||
}
|
||||
|
||||
override fun getType(): PsiType = _parameterType
|
||||
|
||||
init {
|
||||
//We should force computations on all lazy delegates to release descriptor on the end of ctor call
|
||||
with(lazyInitializers) {
|
||||
forEach { it.value }
|
||||
clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,53 +51,55 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import java.text.StringCharacterIterator
|
||||
|
||||
internal fun buildTypeParameterList(
|
||||
declaration: CallableMemberDescriptor,
|
||||
owner: PsiTypeParameterListOwner,
|
||||
support: KtUltraLightSupport
|
||||
): PsiTypeParameterList = buildTypeParameterList(
|
||||
declaration, owner, support,
|
||||
object : TypeParametersSupport<CallableMemberDescriptor, TypeParameterDescriptor> {
|
||||
override fun parameters(declaration: CallableMemberDescriptor) = declaration.typeParameters
|
||||
|
||||
override fun name(typeParameter: TypeParameterDescriptor) = typeParameter.name.asString()
|
||||
|
||||
override fun hasNonTrivialBounds(
|
||||
declaration: CallableMemberDescriptor,
|
||||
typeParameter: TypeParameterDescriptor
|
||||
) = typeParameter.upperBounds.any { !KotlinBuiltIns.isDefaultBound(it) }
|
||||
|
||||
override fun asDescriptor(typeParameter: TypeParameterDescriptor) = typeParameter
|
||||
}
|
||||
)
|
||||
|
||||
internal fun buildTypeParameterList(
|
||||
declaration: KtTypeParameterListOwner,
|
||||
owner: PsiTypeParameterListOwner,
|
||||
support: KtUltraLightSupport
|
||||
): PsiTypeParameterList = buildTypeParameterList(
|
||||
declaration, owner, support,
|
||||
object : TypeParametersSupport<KtTypeParameterListOwner, KtTypeParameter> {
|
||||
override fun parameters(declaration: KtTypeParameterListOwner) = declaration.typeParameters
|
||||
override fun name(typeParameter: KtTypeParameter) = typeParameter.name
|
||||
|
||||
override fun hasNonTrivialBounds(
|
||||
declaration: KtTypeParameterListOwner,
|
||||
typeParameter: KtTypeParameter
|
||||
) = typeParameter.extendsBound != null || declaration.typeConstraints.isNotEmpty()
|
||||
|
||||
override fun asDescriptor(typeParameter: KtTypeParameter) = typeParameter.resolve() as? TypeParameterDescriptor
|
||||
}
|
||||
)
|
||||
|
||||
interface TypeParametersSupport<D, T> {
|
||||
private interface TypeParametersSupport<D, T> {
|
||||
fun parameters(declaration: D): List<T>
|
||||
fun name(typeParameter: T): String?
|
||||
fun hasNonTrivialBounds(declaration: D, typeParameter: T): Boolean
|
||||
fun asDescriptor(typeParameter: T): TypeParameterDescriptor?
|
||||
}
|
||||
|
||||
internal fun <D, T> buildTypeParameterList(
|
||||
private val supportForDescriptor = object : TypeParametersSupport<CallableMemberDescriptor, TypeParameterDescriptor> {
|
||||
|
||||
override fun parameters(declaration: CallableMemberDescriptor) = declaration.typeParameters
|
||||
|
||||
override fun name(typeParameter: TypeParameterDescriptor) = typeParameter.name.asString()
|
||||
|
||||
override fun hasNonTrivialBounds(
|
||||
declaration: CallableMemberDescriptor,
|
||||
typeParameter: TypeParameterDescriptor
|
||||
) = typeParameter.upperBounds.any { !KotlinBuiltIns.isDefaultBound(it) }
|
||||
|
||||
override fun asDescriptor(typeParameter: TypeParameterDescriptor) = typeParameter
|
||||
}
|
||||
|
||||
private val supportForSourceDeclaration = object : TypeParametersSupport<KtTypeParameterListOwner, KtTypeParameter> {
|
||||
|
||||
override fun parameters(declaration: KtTypeParameterListOwner) = declaration.typeParameters
|
||||
|
||||
override fun name(typeParameter: KtTypeParameter) = typeParameter.name
|
||||
|
||||
override fun hasNonTrivialBounds(
|
||||
declaration: KtTypeParameterListOwner,
|
||||
typeParameter: KtTypeParameter
|
||||
) = typeParameter.extendsBound != null || declaration.typeConstraints.isNotEmpty()
|
||||
|
||||
override fun asDescriptor(typeParameter: KtTypeParameter) = typeParameter.resolve() as? TypeParameterDescriptor
|
||||
}
|
||||
|
||||
internal fun buildTypeParameterListForDescriptor(
|
||||
declaration: CallableMemberDescriptor,
|
||||
owner: PsiTypeParameterListOwner,
|
||||
support: KtUltraLightSupport
|
||||
): PsiTypeParameterList = buildTypeParameterList(declaration, owner, support, supportForDescriptor)
|
||||
|
||||
|
||||
internal fun buildTypeParameterListForSourceDeclaration(
|
||||
declaration: KtTypeParameterListOwner,
|
||||
owner: PsiTypeParameterListOwner,
|
||||
support: KtUltraLightSupport
|
||||
): PsiTypeParameterList = buildTypeParameterList(declaration, owner, support, supportForSourceDeclaration)
|
||||
|
||||
private fun <D, T> buildTypeParameterList(
|
||||
declaration: D,
|
||||
owner: PsiTypeParameterListOwner,
|
||||
support: KtUltraLightSupport,
|
||||
@@ -226,35 +228,15 @@ fun KtUltraLightClass.createGeneratedMethodFromDescriptor(
|
||||
descriptor: FunctionDescriptor,
|
||||
declarationForOrigin: KtDeclaration? = null
|
||||
): KtLightMethod {
|
||||
val lightMethod = lightMethod(descriptor)
|
||||
|
||||
val kotlinOrigin =
|
||||
declarationForOrigin
|
||||
?: DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? KtDeclaration
|
||||
?: kotlinOrigin
|
||||
|
||||
val lightMemberOrigin = LightMemberOriginForDeclaration(kotlinOrigin, JvmDeclarationOriginKind.OTHER)
|
||||
val wrapper = KtUltraLightMethodForDescriptor(descriptor, lightMethod, lightMemberOrigin, support, this)
|
||||
|
||||
descriptor.extensionReceiverParameter?.let { receiver ->
|
||||
lightMethod.addParameter(KtUltraLightParameterForDescriptor(receiver, support, wrapper))
|
||||
}
|
||||
|
||||
for (valueParameter in descriptor.valueParameters) {
|
||||
lightMethod.addParameter(KtUltraLightParameterForDescriptor(valueParameter, support, wrapper))
|
||||
}
|
||||
|
||||
if (descriptor is ConstructorDescriptor) {
|
||||
lightMethod.isConstructor = true
|
||||
lightMethod.setMethodReturnType(PsiType.VOID)
|
||||
} else {
|
||||
lightMethod.setMethodReturnType {
|
||||
support.mapType(wrapper) { typeMapper, signatureWriter ->
|
||||
typeMapper.mapReturnType(descriptor, signatureWriter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wrapper
|
||||
return KtUltraLightMethodForDescriptor(descriptor, lightMethod(descriptor), lightMemberOrigin, support, this)
|
||||
}
|
||||
|
||||
private fun KtUltraLightClass.lightMethod(
|
||||
|
||||
+7
-19
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
* Copyright 2010-2019 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.idea.caches.resolve
|
||||
@@ -65,9 +54,9 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
||||
override val isReleasedCoroutine
|
||||
get() = module?.languageVersionSettings?.supportsFeature(LanguageFeature.ReleaseCoroutines) ?: true
|
||||
|
||||
override val moduleDescriptor by lazyPub {
|
||||
element.getResolutionFacade().moduleDescriptor
|
||||
}
|
||||
private val resolutionFacade get() = element.getResolutionFacade()
|
||||
|
||||
override val moduleDescriptor get() = resolutionFacade.moduleDescriptor
|
||||
|
||||
override val moduleName: String by lazyPub {
|
||||
JvmCodegenUtil.getModuleName(moduleDescriptor)
|
||||
@@ -102,9 +91,8 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
||||
return null
|
||||
}
|
||||
|
||||
override val deprecationResolver: DeprecationResolver by lazyPub {
|
||||
element.getResolutionFacade().getFrontendService(DeprecationResolver::class.java)
|
||||
}
|
||||
override val deprecationResolver: DeprecationResolver get() = resolutionFacade.getFrontendService(DeprecationResolver::class.java)
|
||||
|
||||
|
||||
override val typeMapper: KotlinTypeMapper by lazyPub {
|
||||
KotlinTypeMapper(
|
||||
|
||||
+8
-2
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.asJava.classes
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.idea.perf.UltraLightChecker
|
||||
import org.jetbrains.kotlin.idea.perf.UltraLightChecker.checkDescriptorsLeak
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -30,7 +31,9 @@ abstract class AbstractUltraLightClassLoadingTest : KotlinLightCodeInsightFixtur
|
||||
LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass)?.let { it to ktClass }
|
||||
}.joinToString("\n\n") { (ultraLightClass, ktClass) ->
|
||||
with(UltraLightChecker) {
|
||||
ultraLightClass.renderClass()
|
||||
ultraLightClass.renderClass().also {
|
||||
checkDescriptorsLeak(ultraLightClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +42,10 @@ abstract class AbstractUltraLightClassLoadingTest : KotlinLightCodeInsightFixtur
|
||||
}
|
||||
|
||||
for (ktClass in UltraLightChecker.allClasses(file)) {
|
||||
UltraLightChecker.checkClassEquivalence(ktClass)
|
||||
val ultraLightClass = UltraLightChecker.checkClassEquivalence(ktClass)
|
||||
if (ultraLightClass != null) {
|
||||
checkDescriptorsLeak(ultraLightClass)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-1
@@ -33,7 +33,10 @@ abstract class AbstractUltraLightFacadeClassTest : KotlinLightCodeInsightFixture
|
||||
val facades = KotlinAsJavaSupport.getInstance(project).getFacadeNames(FqName.ROOT, scope)
|
||||
|
||||
for (facadeName in facades) {
|
||||
UltraLightChecker.checkFacadeEquivalence(FqName(facadeName), scope, project)
|
||||
val ultraLightClass = UltraLightChecker.checkFacadeEquivalence(FqName(facadeName), scope, project)
|
||||
if (ultraLightClass != null) {
|
||||
UltraLightChecker.checkDescriptorsLeak(ultraLightClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,23 +8,29 @@ package org.jetbrains.kotlin.idea.perf
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Conditions
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.light.LightElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import com.intellij.util.PairProcessor
|
||||
import com.intellij.util.ref.DebugReflectionUtil
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.classes.*
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightPsiArrayInitializerMemberValue
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.load.kotlin.NON_EXISTENT_CLASS_NAME
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.Assert
|
||||
import kotlin.test.assertFails
|
||||
|
||||
fun UsefulTestCase.forceUsingOldLightClassesForTest() {
|
||||
KtUltraLightSupport.forceUsingOldLightClasses = true
|
||||
@@ -263,4 +269,29 @@ object UltraLightChecker {
|
||||
}
|
||||
|
||||
private fun String.prependDefaultIndent() = prependIndent(" ")
|
||||
}
|
||||
|
||||
private fun checkDescriptorLeakOnElement(element: PsiElement) {
|
||||
DebugReflectionUtil.walkObjects(
|
||||
10,
|
||||
mapOf(element to element.javaClass.name),
|
||||
Any::class.java,
|
||||
Conditions.alwaysTrue(),
|
||||
PairProcessor { value, backLink ->
|
||||
if (value is DeclarationDescriptor) {
|
||||
assertFails {
|
||||
"""Leaked descriptor ${value.javaClass.name} in ${element.javaClass.name}\n$backLink"""
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
fun checkDescriptorsLeak(lightClass: KtLightClass) {
|
||||
checkDescriptorLeakOnElement(lightClass)
|
||||
lightClass.methods.forEach {
|
||||
checkDescriptorLeakOnElement(it)
|
||||
it.parameterList.parameters.forEach { parameter -> checkDescriptorLeakOnElement(parameter) }
|
||||
}
|
||||
lightClass.fields.forEach { checkDescriptorLeakOnElement(it) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user