diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt index 49a3dee7326..9bbdc0e7a89 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightAnnotations.kt @@ -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>(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) = diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt index cb33226bff9..2326de137cf 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt @@ -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 = PsiClassImplUtil.getInterfaces(this) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightElementWithNullabilityAnnotation.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightElementWithNullabilityAnnotation.kt index e6bc8a87ac6..70ca12198e4 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightElementWithNullabilityAnnotation.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightElementWithNullabilityAnnotation.kt @@ -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 : KtLightDeclaration, 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? } \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt index 1c10987b98c..cb257b80098 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt @@ -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 diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt index a4c744d0e3d..0f7bd4488c2 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt @@ -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 { + 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 = 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>() + private inline fun getAndAddLazy(crossinline initializer: () -> T): Lazy = + lazyPub { initializer() }.also { lazyInitializers.add(it) } - override val givenAnnotations: List - 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 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() + } + } } \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt index 2e5cd485152..ea2f28f82f8 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt @@ -27,16 +27,19 @@ internal class KtUltraLightSuspendContinuationParameter( KtLightParameter, KtUltraLightElementWithNullabilityAnnotation { - 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, 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>() + private inline fun getAndAddLazy(crossinline initializer: () -> T): Lazy = + 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 - get() = descriptor.obtainLightAnnotations(support, this) + private val _isVarArgs: Boolean by getAndAddLazy { + (descriptor as? ValueParameterDescriptor)?.varargElementType != null + } + + override fun isVarArgs() = _isVarArgs + + override val givenAnnotations: List 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() + } + } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt index ef8484e5605..ec5b2c3be1f 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt @@ -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 { - 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 { - 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 { +private interface TypeParametersSupport { fun parameters(declaration: D): List fun name(typeParameter: T): String? fun hasNonTrivialBounds(declaration: D, typeParameter: T): Boolean fun asDescriptor(typeParameter: T): TypeParameterDescriptor? } -internal fun buildTypeParameterList( +private val supportForDescriptor = object : TypeParametersSupport { + + 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 { + + 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 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( diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index 35809f00c4e..3c4696764ef 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -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( diff --git a/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt index f8333b8212f..11950671565 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt @@ -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) + } } } diff --git a/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightFacadeClassLoadingTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightFacadeClassLoadingTest.kt index 8c61245f7a6..01e003506c6 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightFacadeClassLoadingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightFacadeClassLoadingTest.kt @@ -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) + } } } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/perf/UltraLightChecker.kt b/idea/tests/org/jetbrains/kotlin/idea/perf/UltraLightChecker.kt index 921f4f44f94..f6dcb3eafab 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/perf/UltraLightChecker.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/perf/UltraLightChecker.kt @@ -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) } + } +} \ No newline at end of file