LazyLightClassDataHolder: rollback optimization on suspected inherited internal visibility

Internal visibility affects member names
This commit is contained in:
Pavel V. Talanov
2017-03-24 18:02:34 +03:00
parent ec895f18f2
commit e84a674504
@@ -16,18 +16,27 @@
package org.jetbrains.kotlin.idea.caches.resolve.lightClasses package org.jetbrains.kotlin.idea.caches.resolve.lightClasses
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiClassType import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.search.EverythingGlobalScope
import com.intellij.psi.stubs.StubIndex
import org.jetbrains.kotlin.asJava.LightClassBuilder import org.jetbrains.kotlin.asJava.LightClassBuilder
import org.jetbrains.kotlin.asJava.builder.* import org.jetbrains.kotlin.asJava.builder.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.elements.KtLightField import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl
import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl
import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.idea.stubindex.KotlinOverridableInternalMembersShortNameIndex
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration
typealias LightClassContextProvider = () -> LightClassConstructionContext typealias LightClassContextProvider = () -> LightClassConstructionContext
@@ -82,12 +91,16 @@ sealed class LazyLightClassDataHolder(
if (dummyDelegate == null) return KtLightFieldImpl.fromClsFields(clsDelegate, containingClass) if (dummyDelegate == null) return KtLightFieldImpl.fromClsFields(clsDelegate, containingClass)
return dummyDelegate!!.fields.map { dummyField -> return dummyDelegate!!.fields.map { dummyField ->
val memberOrigin = ClsWrapperStubPsiFactory.getMemberOrigin(dummyField)!! val fieldOrigin = KtLightFieldImpl.getOrigin(dummyField)!!
if (shouldRollbackOptimization(fieldOrigin)) {
return@getOwnFields KtLightFieldImpl.fromClsFields(clsDelegate, containingClass)
}
val fieldName = dummyField.name!! val fieldName = dummyField.name!!
KtLightFieldImpl.lazy(dummyField, memberOrigin, containingClass) { KtLightFieldImpl.lazy(dummyField, fieldOrigin, containingClass) {
clsDelegate.findFieldByName(fieldName, false)!!.apply { clsDelegate.findFieldByName(fieldName, false)?.apply { assert(this.memberIndex!! == dummyField.memberIndex!!) }
assert(this.memberIndex!! == dummyField.memberIndex!!) ?: throw LazyLightClassMemberMatchingError(dummyField, containingClass)
}
} }
} }
} }
@@ -96,14 +109,26 @@ sealed class LazyLightClassDataHolder(
if (dummyDelegate == null) return KtLightMethodImpl.fromClsMethods(clsDelegate, containingClass) if (dummyDelegate == null) return KtLightMethodImpl.fromClsMethods(clsDelegate, containingClass)
return dummyDelegate!!.methods.map { dummyMethod -> return dummyDelegate!!.methods.map { dummyMethod ->
val methodName = dummyMethod.name val methodOrigin = KtLightMethodImpl.getOrigin(dummyMethod)
KtLightMethodImpl.lazy(dummyMethod, containingClass, ClsWrapperStubPsiFactory.getMemberOrigin(dummyMethod)) {
if (shouldRollbackOptimization(methodOrigin)) {
return@getOwnMethods KtLightMethodImpl.fromClsMethods(clsDelegate, containingClass)
}
KtLightMethodImpl.lazy(dummyMethod, containingClass, methodOrigin) {
val dummyIndex = dummyMethod.memberIndex!! val dummyIndex = dummyMethod.memberIndex!!
clsDelegate.findMethodsByName(methodName, false).filter {
delegateCandidate -> delegateCandidate.memberIndex == dummyIndex val byMemberIndex: (PsiMethod) -> Boolean = { it.memberIndex == dummyIndex }
}.single().apply {
assert(this.parameterList.parametersCount == dummyMethod.parameterList.parametersCount) /* Searching all methods may be necessary in some cases where we failed to rollback optimization:
} Overriding internal member that was final
Resulting light member is not consistent in this case, so this should happen only for erroneous code
*/
val exactDelegateMethod = clsDelegate.findMethodsByName(dummyMethod.name, false).firstOrNull(byMemberIndex)
?: clsDelegate.methods.firstOrNull(byMemberIndex)
exactDelegateMethod?.apply { assert(this.parameterList.parametersCount == dummyMethod.parameterList.parametersCount) }
?: throw LazyLightClassMemberMatchingError(dummyMethod, containingClass)
} }
} }
} }
@@ -113,3 +138,29 @@ sealed class LazyLightClassDataHolder(
} }
} }
private fun shouldRollbackOptimization(origin: LightMemberOriginForDeclaration?): Boolean {
val kotlinDeclaration = origin?.originalElement as? KtNamedDeclaration ?: return false
if (!kotlinDeclaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false
val possiblyOverridesInternalMember = kotlinDeclaration.name?.let { anyInternalMembersWithThisName(it, kotlinDeclaration.project) } ?: false
return possiblyOverridesInternalMember
}
private fun anyInternalMembersWithThisName(name: String, project: Project): Boolean {
var result = false
StubIndex.getInstance().processElements(
KotlinOverridableInternalMembersShortNameIndex.Instance.key, name, project,
EverythingGlobalScope(project), KtCallableDeclaration::class.java
) {
result = true
false // stop processing at first matching result
}
return result
}
private class LazyLightClassMemberMatchingError(dummyMember: PsiMember, containingClass: KtLightClass)
: AssertionError("Error matching ${dummyMember::class.simpleName}:${dummyMember.name} created for $containingClass")