Fix invalid equality for UL methods

Potentially fixed #KT-34796
This commit is contained in:
Igor Yakovlev
2019-12-03 20:54:05 +03:00
parent d6fcde7316
commit 2639ed2c48
4 changed files with 84 additions and 24 deletions
@@ -403,7 +403,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
} }
val primary = classOrObject.primaryConstructor val primary = classOrObject.primaryConstructor
if (primary != null && shouldGenerateNoArgOverload(primary)) { if (primary != null && shouldGenerateNoArgOverload(primary)) {
result.add(noArgConstructor(primary.simpleVisibility(), primary)) result.add(noArgConstructor(primary.simpleVisibility(), primary, METHOD_INDEX_FOR_NO_ARG_OVERLOAD_CTOR))
} }
return result return result
} }
@@ -425,15 +425,20 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
classOrObject is KtEnumEntry -> PsiModifier.PACKAGE_LOCAL classOrObject is KtEnumEntry -> PsiModifier.PACKAGE_LOCAL
else -> PsiModifier.PUBLIC else -> PsiModifier.PUBLIC
} }
return noArgConstructor(visibility, classOrObject) return noArgConstructor(visibility, classOrObject, METHOD_INDEX_FOR_DEFAULT_CTOR)
} }
private fun noArgConstructor(visibility: String, declaration: KtDeclaration): KtUltraLightMethod = private fun noArgConstructor(
visibility: String,
declaration: KtDeclaration,
methodIndex: Int
): KtUltraLightMethod =
KtUltraLightMethodForSourceDeclaration( KtUltraLightMethodForSourceDeclaration(
LightMethodBuilder(manager, language, name.orEmpty()).setConstructor(true).addModifier(visibility), LightMethodBuilder(manager, language, name.orEmpty()).setConstructor(true).addModifier(visibility),
declaration, declaration,
support, support,
this this,
methodIndex
) )
private fun isHiddenByDeprecation(declaration: KtDeclaration): Boolean { private fun isHiddenByDeprecation(declaration: KtDeclaration): Boolean {
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.asJava.classes package org.jetbrains.kotlin.asJava.classes
import com.intellij.lang.Language
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.light.LightMethodBuilder import com.intellij.psi.impl.light.LightMethodBuilder
import com.intellij.psi.impl.light.LightModifierList import com.intellij.psi.impl.light.LightModifierList
@@ -123,19 +122,22 @@ internal class UltraLightMembersCreator(
return emptyList() return emptyList()
} }
val basicMethod = asJavaMethod(ktFunction, forceStatic, forcePrivate) var methodIndex = METHOD_INDEX_BASE
val basicMethod = asJavaMethod(ktFunction, forceStatic, forcePrivate, methodIndex = methodIndex)
val result = mutableListOf(basicMethod) val result = mutableListOf(basicMethod)
if (ktFunction.hasAnnotation(JVM_OVERLOADS_FQ_NAME)) { if (ktFunction.hasAnnotation(JVM_OVERLOADS_FQ_NAME)) {
val numberOfDefaultParameters = ktFunction.valueParameters.count(KtParameter::hasDefaultValue) val numberOfDefaultParameters = ktFunction.valueParameters.count(KtParameter::hasDefaultValue)
for (numberOfDefaultParametersToAdd in numberOfDefaultParameters - 1 downTo 0) { for (numberOfDefaultParametersToAdd in numberOfDefaultParameters - 1 downTo 0) {
methodIndex++
result.add( result.add(
asJavaMethod( asJavaMethod(
ktFunction, ktFunction,
forceStatic, forceStatic,
forcePrivate, forcePrivate,
numberOfDefaultParametersToAdd = numberOfDefaultParametersToAdd numberOfDefaultParametersToAdd = numberOfDefaultParametersToAdd,
methodIndex = methodIndex
) )
) )
} }
@@ -145,7 +147,7 @@ internal class UltraLightMembersCreator(
} }
internal class KtUltraLightAnnotationMethod( internal class KtUltraLightAnnotationMethod(
psiMethod: KtLightMethod, private val psiMethod: KtLightMethod,
expression: KtExpression expression: KtExpression
) : KtLightMethod by psiMethod, ) : KtLightMethod by psiMethod,
PsiAnnotationMethod { PsiAnnotationMethod {
@@ -154,14 +156,23 @@ internal class UltraLightMembersCreator(
convertToLightAnnotationMemberValue(psiMethod, expression) convertToLightAnnotationMemberValue(psiMethod, expression)
} }
override fun equals(other: Any?): Boolean = psiMethod == (other as? KtUltraLightAnnotationMethod)?.psiMethod
override fun hashCode(): Int = psiMethod.hashCode()
override fun toString(): String = psiMethod.toString()
override fun getDefaultValue(): PsiAnnotationMemberValue? = value override fun getDefaultValue(): PsiAnnotationMemberValue? = value
override fun getSourceElement(): PsiElement? = psiMethod.sourceElement
} }
private fun asJavaMethod( private fun asJavaMethod(
ktFunction: KtFunction, ktFunction: KtFunction,
forceStatic: Boolean, forceStatic: Boolean,
forcePrivate: Boolean, forcePrivate: Boolean,
numberOfDefaultParametersToAdd: Int = -1 numberOfDefaultParametersToAdd: Int = -1,
methodIndex: Int
): KtLightMethod { ): KtLightMethod {
val isConstructor = ktFunction is KtConstructor<*> val isConstructor = ktFunction is KtConstructor<*>
val name = val name =
@@ -169,7 +180,7 @@ internal class UltraLightMembersCreator(
else computeMethodName(ktFunction, ktFunction.name ?: SpecialNames.NO_NAME_PROVIDED.asString(), MethodType.REGULAR) else computeMethodName(ktFunction, ktFunction.name ?: SpecialNames.NO_NAME_PROVIDED.asString(), MethodType.REGULAR)
val method = lightMethod(name.orEmpty(), ktFunction, forceStatic, forcePrivate) val method = lightMethod(name.orEmpty(), ktFunction, forceStatic, forcePrivate)
val wrapper = KtUltraLightMethodForSourceDeclaration(method, ktFunction, support, containingClass) val wrapper = KtUltraLightMethodForSourceDeclaration(method, ktFunction, support, containingClass, methodIndex)
addReceiverParameter(ktFunction, wrapper, method) addReceiverParameter(ktFunction, wrapper, method)
var remainingNumberOfDefaultParametersToAdd = var remainingNumberOfDefaultParametersToAdd =
@@ -266,7 +277,9 @@ internal class UltraLightMembersCreator(
if (forcePrivate || declaration.isPrivate() || accessedProperty?.isPrivate() == true) { if (forcePrivate || declaration.isPrivate() || accessedProperty?.isPrivate() == true) {
return name == PsiModifier.PRIVATE return name == PsiModifier.PRIVATE
} }
if (declaration.hasModifier(KtTokens.PROTECTED_KEYWORD) || accessedProperty?.hasModifier(KtTokens.PROTECTED_KEYWORD) == true) { if (declaration.hasModifier(KtTokens.PROTECTED_KEYWORD) || accessedProperty
?.hasModifier(KtTokens.PROTECTED_KEYWORD) == true
) {
return name == PsiModifier.PROTECTED return name == PsiModifier.PROTECTED
} }
@@ -284,7 +297,8 @@ internal class UltraLightMembersCreator(
return when (name) { return when (name) {
PsiModifier.FINAL -> !containingClass.isInterface && outerDeclaration !is KtConstructor<*> && isFinal(outerDeclaration) PsiModifier.FINAL -> !containingClass.isInterface && outerDeclaration !is KtConstructor<*> && isFinal(outerDeclaration)
PsiModifier.ABSTRACT -> containingClass.isInterface || outerDeclaration.hasModifier(KtTokens.ABSTRACT_KEYWORD) PsiModifier.ABSTRACT -> containingClass.isInterface || outerDeclaration.hasModifier(KtTokens.ABSTRACT_KEYWORD)
PsiModifier.STATIC -> forceStatic || containingClassIsNamedObject && (outerDeclaration.isJvmStatic(support) || declaration.isJvmStatic(support)) PsiModifier.STATIC -> forceStatic || containingClassIsNamedObject && (outerDeclaration.isJvmStatic(support) || declaration
.isJvmStatic(support))
PsiModifier.STRICTFP -> declaration is KtFunction && declaration.hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME) PsiModifier.STRICTFP -> declaration is KtFunction && declaration.hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME)
PsiModifier.SYNCHRONIZED -> declaration is KtFunction && declaration.hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME) PsiModifier.SYNCHRONIZED -> declaration is KtFunction && declaration.hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME)
PsiModifier.NATIVE -> declaration is KtFunction && declaration.hasModifier(EXTERNAL_KEYWORD) PsiModifier.NATIVE -> declaration is KtFunction && declaration.hasModifier(EXTERNAL_KEYWORD)
@@ -408,7 +422,9 @@ internal class UltraLightMembersCreator(
if (declaration is KtProperty && declaration.hasDelegate()) { if (declaration is KtProperty && declaration.hasDelegate()) {
return true return true
} }
if (accessor?.hasModifier(KtTokens.PRIVATE_KEYWORD) == true || accessor?.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) == true) { if (accessor?.hasModifier(KtTokens.PRIVATE_KEYWORD) == true || accessor
?.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) == true
) {
return false return false
} }
if (!isPrivate || accessor?.hasBody() == true) { if (!isPrivate || accessor?.hasBody() == true) {
@@ -436,7 +452,8 @@ internal class UltraLightMembersCreator(
lightMemberOrigin, lightMemberOrigin,
support, support,
containingClass, containingClass,
forceToSkipNullabilityAnnotation = createAsAnnotationMethod forceToSkipNullabilityAnnotation = createAsAnnotationMethod,
methodIndex = METHOD_INDEX_FOR_GETTER
) )
val getterType: PsiType by lazyPub { methodReturnType(declaration, getterWrapper, isSuspendFunction = false) } val getterType: PsiType by lazyPub { methodReturnType(declaration, getterWrapper, isSuspendFunction = false) }
@@ -463,7 +480,13 @@ internal class UltraLightMembersCreator(
val setterPrototype = lightMethod(setterName, auxiliaryOrigin, forceStatic = onlyJvmStatic || forceStatic) val setterPrototype = lightMethod(setterName, auxiliaryOrigin, forceStatic = onlyJvmStatic || forceStatic)
.setMethodReturnType(PsiType.VOID) .setMethodReturnType(PsiType.VOID)
val setterWrapper = KtUltraLightMethodForSourceDeclaration(setterPrototype, lightMemberOrigin, support, containingClass) val setterWrapper = KtUltraLightMethodForSourceDeclaration(
setterPrototype,
lightMemberOrigin,
support,
containingClass,
methodIndex = METHOD_INDEX_FOR_SETTER
)
addReceiverParameter(declaration, setterWrapper, setterPrototype) addReceiverParameter(declaration, setterWrapper, setterPrototype)
val setterParameter = ktSetter?.parameter val setterParameter = ktSetter?.parameter
setterPrototype.addParameter( setterPrototype.addParameter(
@@ -27,11 +27,19 @@ import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
internal const val METHOD_INDEX_FOR_GETTER = 1
internal const val METHOD_INDEX_FOR_SETTER = 2
internal const val METHOD_INDEX_FOR_DEFAULT_CTOR = 3
internal const val METHOD_INDEX_FOR_NO_ARG_OVERLOAD_CTOR = 4
internal const val METHOD_INDEX_FOR_NON_ORIGIN_METHOD = 5
internal const val METHOD_INDEX_BASE = 6
internal abstract class KtUltraLightMethod( internal abstract class KtUltraLightMethod(
internal val delegate: PsiMethod, internal val delegate: PsiMethod,
lightMemberOrigin: LightMemberOrigin?, lightMemberOrigin: LightMemberOrigin?,
protected val support: KtUltraLightSupport, protected val support: KtUltraLightSupport,
containingClass: KtLightClass containingClass: KtLightClass,
private val methodIndex: Int
) : KtLightMethodImpl( ) : KtLightMethodImpl(
{ delegate }, { delegate },
lightMemberOrigin, lightMemberOrigin,
@@ -104,7 +112,15 @@ internal abstract class KtUltraLightMethod(
override fun findSuperMethods(parentClass: PsiClass?): Array<out PsiMethod> = override fun findSuperMethods(parentClass: PsiClass?): Array<out PsiMethod> =
PsiSuperMethodImplUtil.findSuperMethods(this, parentClass) PsiSuperMethodImplUtil.findSuperMethods(this, parentClass)
override fun equals(other: Any?): Boolean = this === other override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KtUltraLightMethod) return false
if (methodIndex != other.methodIndex) return false
if (this.javaClass != other.javaClass) return false
if (containingClass != other.containingClass) return false
if (kotlinOrigin === null || other.kotlinOrigin === null) return false
return kotlinOrigin == other.kotlinOrigin
}
override fun hashCode(): Int = name.hashCode() override fun hashCode(): Int = name.hashCode()
@@ -116,19 +132,29 @@ internal class KtUltraLightMethodForSourceDeclaration(
lightMemberOrigin: LightMemberOrigin?, lightMemberOrigin: LightMemberOrigin?,
support: KtUltraLightSupport, support: KtUltraLightSupport,
containingClass: KtLightClass, containingClass: KtLightClass,
private val forceToSkipNullabilityAnnotation: Boolean = false private val forceToSkipNullabilityAnnotation: Boolean = false,
methodIndex: Int
) : KtUltraLightMethod( ) : KtUltraLightMethod(
delegate, delegate,
lightMemberOrigin, lightMemberOrigin,
support, support,
containingClass containingClass,
methodIndex
) { ) {
constructor( constructor(
delegate: PsiMethod, delegate: PsiMethod,
declaration: KtDeclaration, declaration: KtDeclaration,
support: KtUltraLightSupport, support: KtUltraLightSupport,
containingClass: KtLightClass containingClass: KtLightClass,
) : this(delegate, LightMemberOriginForDeclaration(declaration, JvmDeclarationOriginKind.OTHER), support, containingClass) methodIndex: Int
) : this(
delegate,
LightMemberOriginForDeclaration(declaration, JvmDeclarationOriginKind.OTHER),
support,
containingClass,
forceToSkipNullabilityAnnotation = false,
methodIndex
)
override val qualifiedNameForNullabilityAnnotation: String? override val qualifiedNameForNullabilityAnnotation: String?
get() { get() {
@@ -161,7 +187,8 @@ internal class KtUltraLightMethodForDescriptor(
delegate, delegate,
lightMemberOrigin, lightMemberOrigin,
support, support,
containingClass containingClass,
METHOD_INDEX_FOR_NON_ORIGIN_METHOD
) { ) {
// This is greedy realization of UL class. // 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. // This means that all data that depends on descriptor evaluated in ctor so the descriptor will be released on the end.
@@ -11,9 +11,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Conditions import com.intellij.openapi.util.Conditions
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.light.LightElement
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.UsefulTestCase
import com.intellij.util.PairProcessor import com.intellij.util.PairProcessor
import com.intellij.util.ref.DebugReflectionUtil import com.intellij.util.ref.DebugReflectionUtil
@@ -81,6 +79,13 @@ object UltraLightChecker {
val gold = KtLightClassForSourceDeclaration.createNoCache(ktClass, forceUsingOldLightClasses = true) val gold = KtLightClassForSourceDeclaration.createNoCache(ktClass, forceUsingOldLightClasses = true)
val ultraLightClass = LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass) ?: return null val ultraLightClass = LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass) ?: return null
val secondULInstance = LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass)
Assert.assertNotNull(secondULInstance)
Assert.assertTrue(ultraLightClass !== secondULInstance)
secondULInstance!!
Assert.assertEquals(ultraLightClass.ownMethods.size, secondULInstance.ownMethods.size)
Assert.assertTrue(ultraLightClass.ownMethods.containsAll(secondULInstance.ownMethods))
checkClassEquivalenceByRendering(gold, ultraLightClass) checkClassEquivalenceByRendering(gold, ultraLightClass)
return ultraLightClass return ultraLightClass