diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataHolder.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataHolder.kt index eea59880542..486fc5e3e60 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataHolder.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassDataHolder.kt @@ -17,15 +17,23 @@ package org.jetbrains.kotlin.asJava.builder import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassType import com.intellij.psi.impl.DebugUtil import com.intellij.psi.impl.java.stubs.PsiJavaFileStub import com.intellij.psi.stubs.StubElement import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.LightClassUtil.findClass import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder.javaFileStub +import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject +import org.jetbrains.kotlin.asJava.elements.KtLightField +import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl +import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.debugText.getDebugText import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics interface LightClassDataHolder { @@ -33,15 +41,33 @@ interface LightClassDataHolder { val extraDiagnostics: Diagnostics fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData - fun findData(classOrObject: KtClassOrObject): LightClassData = findData { it.findDelegate(classOrObject) } - fun findData(classFqName: FqName): LightClassData = findData { it.findDelegate(classFqName) } + + fun findDataForDefaultImpls(classOrObject: KtClassOrObject) = findData { + it.findDelegate(classOrObject).findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false) + ?: throw IllegalStateException("Couldn't get delegate for $this\n in ${DebugUtil.stubTreeToString(it)}") + } + + fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData = findData { it.findDelegate(classOrObject) } + fun findDataForFacade(classFqName: FqName): LightClassData = findData { it.findDelegate(classFqName) } } interface LightClassData { val clsDelegate: PsiClass + + val supertypes: Array get() { return clsDelegate.superTypes } + + fun getOwnFields(containingClass: KtLightClass): List + fun getOwnMethods(containingClass: KtLightClass): List } class LightClassDataImpl(override val clsDelegate: PsiClass) : LightClassData { + override fun getOwnFields(containingClass: KtLightClass): List { + return clsDelegate.fields.map { KtLightFieldImpl.fromClsField(it, containingClass) } + } + + override fun getOwnMethods(containingClass: KtLightClass): List { + return clsDelegate.methods.map { KtLightMethodImpl.fromClsMethod(it, containingClass) } + } } object InvalidLightClassDataHolder : LightClassDataHolder { @@ -77,7 +103,7 @@ fun PsiJavaFileStub.findDelegate(classOrObject: KtClassOrObject): PsiClass { } val stubFileText = DebugUtil.stubTreeToString(this) - throw IllegalStateException("Couldn't get delegate for $this\nin $ktFileText\nstub: \n$stubFileText") + throw IllegalStateException("Couldn't get delegate for ${classOrObject.getDebugText()}\nin $ktFileText\nstub: \n$stubFileText") } fun PsiJavaFileStub.findDelegate(classFqName: FqName): PsiClass { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/MemberIndex.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/MemberIndex.kt new file mode 100644 index 00000000000..ceb16d65f79 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/MemberIndex.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.asJava.builder + +import com.intellij.openapi.util.Key +import com.intellij.openapi.util.UserDataHolder +import com.intellij.psi.PsiMember +import com.intellij.psi.StubBasedPsiElement + +data class MemberIndex(private val index: Int) { + companion object { + @JvmField + val KEY = Key.create("MEMBER_INDEX") + } +} + +val PsiMember.memberIndex: MemberIndex? + get() = ((this as? StubBasedPsiElement<*>)?.stub as? UserDataHolder)?.getUserData(MemberIndex.KEY) \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/StubClassBuilder.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/StubClassBuilder.java index 30f68be3aef..5c3c01d58af 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/StubClassBuilder.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/StubClassBuilder.java @@ -26,8 +26,8 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.fileClasses.OldPackageFacadeClassUtils; import org.jetbrains.kotlin.codegen.AbstractClassBuilder; +import org.jetbrains.kotlin.fileClasses.OldPackageFacadeClassUtils; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; @@ -53,6 +53,7 @@ public class StubClassBuilder extends AbstractClassBuilder { private StubBuildingVisitor v; private final Stack parentStack; private boolean isPackageClass = false; + private int memberIndex = 0; public StubClassBuilder(@NotNull Stack parentStack) { this.parentStack = parentStack; @@ -196,6 +197,7 @@ public class StubClassBuilder extends AbstractClassBuilder { } last.putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightMemberOrigin(origin)); + last.putUserData(MemberIndex.KEY, new MemberIndex(memberIndex++)); } @Override diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLazyLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLazyLightClass.kt new file mode 100644 index 00000000000..fbc481a82f0 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLazyLightClass.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.asJava.classes + +import com.intellij.psi.PsiManager +import org.jetbrains.kotlin.asJava.builder.LightClassData + +abstract class KtLazyLightClass(manager: PsiManager) : KtLightClassBase(manager) { + abstract val lightClassData: LightClassData + + + override fun getOwnFields() = lightClassData.getOwnFields(this) + override fun getOwnMethods() = lightClassData.getOwnMethods(this) + override fun getSuperTypes() = lightClassData.supertypes + +} \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt index 1a4ca524c26..964918faccd 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForFacade.kt @@ -51,7 +51,7 @@ class KtLightClassForFacade private constructor( private val facadeClassFqName: FqName, private val lightClassDataCache: CachedValue, files: Collection -) : KtLightClassBase(manager) { +) : KtLazyLightClass(manager) { private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope) class FacadeStubCache(private val project: Project) { @@ -194,8 +194,12 @@ class KtLightClassForFacade private constructor( override fun copy() = KtLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, files) + override val lightClassData by lazy(LazyThreadSafetyMode.PUBLICATION) { + lightClassDataCache.value.findDataForFacade(facadeClassFqName) + } + override val clsDelegate: PsiClass - get() = lightClassDataCache.value.findData(facadeClassFqName).clsDelegate + get() = lightClassData.clsDelegate override fun getNavigationElement() = files.iterator().next() diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt index 59dcc96dc24..5e6e6586d70 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForInterfaceDefaultImpls.kt @@ -17,15 +17,12 @@ package org.jetbrains.kotlin.asJava.classes import com.intellij.psi.* -import com.intellij.psi.impl.DebugUtil import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.asJava.builder.LightClassData -import org.jetbrains.kotlin.asJava.builder.findDelegate import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.psi.KtClassOrObject -class KtLightClassForInterfaceDefaultImpls( - classOrObject: KtClassOrObject) +class KtLightClassForInterfaceDefaultImpls(classOrObject: KtClassOrObject) : KtLightClassForSourceDeclaration(classOrObject) { override fun getQualifiedName(): String? = containingClass?.qualifiedName?.let { it + ".${JvmAbi.DEFAULT_IMPLS_CLASS_NAME}" } @@ -37,10 +34,7 @@ class KtLightClassForInterfaceDefaultImpls( } override fun findLightClassData(): LightClassData { - return getLightClassDataHolder().findData { - it.findDelegate(classOrObject).findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false) - ?: throw IllegalStateException("Couldn't get delegate for $this\n in ${DebugUtil.stubTreeToString(it)}") - } + return getLightClassDataHolder().findDataForDefaultImpls(classOrObject) } override fun getTypeParameterList(): PsiTypeParameterList? = null diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt index 49d394ad010..9f65ef5ecf6 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt @@ -59,7 +59,7 @@ import java.util.* import javax.swing.Icon abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtClassOrObject) - : KtLightClassBase(classOrObject.manager), StubBasedPsiElement> { + : KtLazyLightClass(classOrObject.manager), StubBasedPsiElement> { private val lightIdentifier = KtLightIdentifier(this, classOrObject) private val _extendsList by lazy(LazyThreadSafetyMode.PUBLICATION) { @@ -80,9 +80,9 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC override val clsDelegate: PsiClass get() = lightClassData.clsDelegate - private val lightClassData: LightClassData by lazy(LazyThreadSafetyMode.PUBLICATION) { findLightClassData() } + override val lightClassData: LightClassData by lazy(LazyThreadSafetyMode.PUBLICATION) { findLightClassData() } - open protected fun findLightClassData() = getLightClassDataHolder().findData(classOrObject) + open protected fun findLightClassData() = getLightClassDataHolder().findDataForClassOrObject(classOrObject) private fun getJavaFileStub(): PsiJavaFileStub = getLightClassDataHolder().javaFileStub diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightFieldImpl.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightFieldImpl.kt index 5b12cea0abc..a50610d918c 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightFieldImpl.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightFieldImpl.kt @@ -36,13 +36,16 @@ import org.jetbrains.kotlin.psi.KtNamedDeclaration import java.lang.UnsupportedOperationException // Copied from com.intellij.psi.impl.light.LightField -sealed class KtLightFieldImpl( +sealed class KtLightFieldImpl( override val lightMemberOrigin: LightMemberOrigin?, - override val clsDelegate: PsiField, - private val containingClass: KtLightClass -) : LightElement(clsDelegate.manager, KotlinLanguage.INSTANCE), KtLightField { + computeRealDelegate: () -> T, + private val containingClass: KtLightClass, + private val dummyDelegate: PsiField? +) : LightElement(containingClass.manager, KotlinLanguage.INSTANCE), KtLightField { private val lightIdentifier by lazy(LazyThreadSafetyMode.PUBLICATION) { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) } + override val clsDelegate: T by lazy(LazyThreadSafetyMode.PUBLICATION, computeRealDelegate) + @Throws(IncorrectOperationException::class) override fun setInitializer(initializer: PsiExpression?) = throw IncorrectOperationException("Not supported") @@ -50,7 +53,7 @@ sealed class KtLightFieldImpl( override fun getPresentation(): ItemPresentation? = (kotlinOrigin ?: this).let { ItemPresentationProviders.getItemPresentation(it) } - override fun getName() = clsDelegate.name + override fun getName() = dummyDelegate?.name ?: clsDelegate.name override fun getNameIdentifier() = lightIdentifier @@ -134,41 +137,58 @@ sealed class KtLightFieldImpl( class KtLightEnumConstant( origin: LightMemberOrigin?, - override val clsDelegate: PsiEnumConstant, + computeDelegate: () -> PsiEnumConstant, containingClass: KtLightClass, - private val initializingClass: PsiEnumConstantInitializer? - ) : KtLightFieldImpl(origin, clsDelegate, containingClass), PsiEnumConstant { + dummyDelegate: PsiField? + ) : KtLightFieldImpl(origin, computeDelegate , containingClass, dummyDelegate), PsiEnumConstant { + private val initializingClass by lazy(LazyThreadSafetyMode.PUBLICATION) { + val kotlinEnumEntry = (lightMemberOrigin as? LightMemberOriginForDeclaration)?.originalElement as? KtEnumEntry + if (kotlinEnumEntry != null && kotlinEnumEntry.declarations.isNotEmpty()) { + KtLightClassForEnumEntry(kotlinEnumEntry, clsDelegate) + } + else null + } + // NOTE: we don't use "delegation by" because the compiler would generate method calls to ALL of PsiEnumConstant members, // but we need only members whose implementations are not present in KotlinLightField override fun getArgumentList() = clsDelegate.argumentList override fun getInitializingClass(): PsiEnumConstantInitializer? = initializingClass - override fun getOrCreateInitializingClass(): PsiEnumConstantInitializer = - initializingClass ?: throw UnsupportedOperationException("Can't create enum constant body: ${clsDelegate.name}") + override fun getOrCreateInitializingClass(): PsiEnumConstantInitializer { + return initializingClass ?: throw UnsupportedOperationException("Can't create enum constant body: ${clsDelegate.name}") + } override fun resolveConstructor() = clsDelegate.resolveConstructor() override fun resolveMethod() = clsDelegate.resolveMethod() override fun resolveMethodGenerics() = clsDelegate.resolveMethodGenerics() } - class KtLightFieldForDeclaration(origin: LightMemberOrigin?, delegate: PsiField, containingClass: KtLightClass) : - KtLightFieldImpl(origin, delegate, containingClass) + class KtLightFieldForDeclaration(origin: LightMemberOrigin?, computeDelegate: () -> PsiField, containingClass: KtLightClass, dummyDelegate: PsiField?) : + KtLightFieldImpl(origin, computeDelegate, containingClass, dummyDelegate) companion object Factory { fun create(origin: LightMemberOrigin?, delegate: PsiField, containingClass: KtLightClass): KtLightField { when (delegate) { is PsiEnumConstant -> { - val kotlinEnumEntry = (origin as? LightMemberOriginForDeclaration)?.originalElement as? KtEnumEntry - val initializingClass = if (kotlinEnumEntry != null && kotlinEnumEntry.declarations.isNotEmpty()) { - KtLightClassForEnumEntry(kotlinEnumEntry, delegate) - } - else null - return KtLightEnumConstant(origin, delegate, containingClass, initializingClass) + return KtLightEnumConstant(origin, { delegate }, containingClass, null) } - else -> return KtLightFieldForDeclaration(origin, delegate, containingClass) + else -> return KtLightFieldForDeclaration(origin, { delegate }, containingClass, null) } } + fun lazy( + dummyDelegate: PsiField, + origin: LightMemberOriginForDeclaration, + containingClass: KtLightClass, + computeRealDelegate: () -> PsiField + ): KtLightField { + if (dummyDelegate is PsiEnumConstant) { + @Suppress("UNCHECKED_CAST") + return KtLightEnumConstant(origin, computeRealDelegate as () -> PsiEnumConstant, containingClass, dummyDelegate) + } + return KtLightFieldForDeclaration(origin, computeRealDelegate, containingClass, dummyDelegate) + } + @JvmStatic fun fromClsField(field: PsiField, containingClass: KtLightClass): KtLightField { val origin = ClsWrapperStubPsiFactory.getMemberOrigin(field) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMethod.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMethod.kt index 02254ef5446..f10b926106a 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMethod.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMethod.kt @@ -21,8 +21,8 @@ import com.intellij.navigation.ItemPresentation import com.intellij.openapi.util.TextRange import com.intellij.psi.* import com.intellij.psi.impl.compiled.ClsTypeElementImpl -import com.intellij.psi.impl.light.LightMethod -import com.intellij.psi.impl.light.LightModifierList +import com.intellij.psi.impl.light.LightElement +import com.intellij.psi.javadoc.PsiDocComment import com.intellij.psi.scope.PsiScopeProcessor import com.intellij.psi.util.* import com.intellij.util.IncorrectOperationException @@ -35,22 +35,26 @@ import org.jetbrains.kotlin.asJava.propertyNameByAccessor import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind -interface KtLightMethod : PsiMethod, KtLightDeclaration { +interface KtLightMethod : PsiAnnotationMethod, KtLightDeclaration { val lightMethodOrigin: LightMemberOrigin? val isDelegated: Boolean } -sealed class KtLightMethodImpl( - override val clsDelegate: PsiMethod, +class KtLightMethodImpl private constructor( + computeRealDelegate: () -> PsiMethod, override val lightMethodOrigin: LightMemberOrigin?, - containingClass: KtLightClass -) : LightMethod(clsDelegate.manager, clsDelegate, containingClass), KtLightMethod { + private val containingClass: KtLightClass, + private val dummyDelegate: PsiMethod? = null +) : LightElement(containingClass.manager, containingClass.language), KtLightMethod { override val kotlinOrigin: KtDeclaration? get() = lightMethodOrigin?.originalElement as? KtDeclaration + override val clsDelegate by lazy(LazyThreadSafetyMode.PUBLICATION, computeRealDelegate) + private val lightIdentifier by lazy(LazyThreadSafetyMode.PUBLICATION) { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) } private val returnTypeElem by lazy(LazyThreadSafetyMode.PUBLICATION) { val delegateTypeElement = clsDelegate.returnTypeElement as? ClsTypeElementImpl @@ -59,7 +63,7 @@ sealed class KtLightMethodImpl( private val calculatingReturnType = ThreadLocal() - override fun getContainingClass(): KtLightClass = super.getContainingClass() as KtLightClass + override fun getContainingClass(): KtLightClass = containingClass private val paramsList: CachedValue by lazy(LazyThreadSafetyMode.PUBLICATION) { val cacheManager = CachedValuesManager.getManager(clsDelegate.project) @@ -95,7 +99,7 @@ sealed class KtLightMethodImpl( override fun getNavigationElement(): PsiElement = kotlinOrigin?.navigationElement ?: super.getNavigationElement() override fun getPresentation(): ItemPresentation? = kotlinOrigin?.presentation ?: super.getPresentation() - override fun getParent(): PsiElement? = containingClass + override fun getParent(): PsiElement = containingClass override fun getText() = kotlinOrigin?.text ?: "" override fun getTextRange() = kotlinOrigin?.textRange ?: TextRange.EMPTY_RANGE @@ -198,21 +202,11 @@ sealed class KtLightMethodImpl( containingClass == other.containingClass && clsDelegate == other.clsDelegate - override fun hashCode(): Int = ((name.hashCode() * 31 + (lightMethodOrigin?.hashCode() ?: 0)) * 31 + containingClass.hashCode()) * 31 + clsDelegate.hashCode() + override fun hashCode(): Int = ((getName().hashCode() * 31 + (lightMethodOrigin?.hashCode() ?: 0)) * 31 + containingClass.hashCode()) * 31 + clsDelegate.hashCode() override fun toString(): String = "${this::class.java.simpleName}:$name" - private class KtLightMethodForDeclaration( - delegate: PsiMethod, origin: LightMemberOrigin?, containingClass: KtLightClass - ) : KtLightMethodImpl(delegate, origin, containingClass) - - class KtLightAnnotationMethod( - override val clsDelegate: PsiAnnotationMethod, - origin: LightMemberOrigin?, - containingClass: KtLightClass - ) : KtLightMethodImpl(clsDelegate, origin, containingClass), PsiAnnotationMethod { - override fun getDefaultValue() = clsDelegate.defaultValue - } + override fun getDefaultValue() = (clsDelegate as? PsiAnnotationMethod)?.defaultValue // override getReturnType() so return type resolves to type parameters of this method not delegate's // which is relied upon by java type inference @@ -229,27 +223,72 @@ sealed class KtLightMethodImpl( } companion object Factory { + private fun adjustMethodOrigin(origin: LightMemberOriginForDeclaration?): LightMemberOriginForDeclaration? { + val originalElement = origin?.originalElement + if (originalElement is KtPropertyAccessor) { + return origin.copy(originalElement.getStrictParentOfType()!!, origin.originKind) + } + return origin + } + fun create( delegate: PsiMethod, origin: LightMemberOrigin?, containingClass: KtLightClass ): KtLightMethodImpl { - return when (delegate) { - is PsiAnnotationMethod -> KtLightAnnotationMethod(delegate, origin, containingClass) - else -> KtLightMethodForDeclaration(delegate, origin, containingClass) - } + return KtLightMethodImpl({ delegate}, origin, containingClass) } @JvmStatic fun fromClsMethod(method: PsiMethod, containingClass: KtLightClass): KtLightMethodImpl { - var origin = ClsWrapperStubPsiFactory.getMemberOrigin(method) - val originalElement = if (origin != null) origin.originalElement else null - if (originalElement is KtPropertyAccessor) { + val origin = ClsWrapperStubPsiFactory.getMemberOrigin(method) + return KtLightMethodImpl.create(method, adjustMethodOrigin(origin), containingClass) + } - origin = origin!!.copy(PsiTreeUtil.getParentOfType(originalElement, KtProperty::class.java)!!, origin.originKind) - } - - return KtLightMethodImpl.create(method, origin, containingClass) + @JvmStatic + fun lazy( + dummyDelegate: PsiMethod?, + containingClass: KtLightClass, + origin: LightMemberOriginForDeclaration?, + computeRealDelegate: () -> PsiMethod + ): KtLightMethodImpl { + return KtLightMethodImpl(computeRealDelegate, adjustMethodOrigin(origin), containingClass, dummyDelegate) } } + + override fun getName() = dummyDelegate?.name ?: clsDelegate.name + + override fun hasModifierProperty(name: String) = clsDelegate.hasModifierProperty(name) + + override fun getThrowsList() = clsDelegate.throwsList + + override fun hasTypeParameters() = clsDelegate.hasTypeParameters() + + override fun isVarArgs() = clsDelegate.isVarArgs + + override fun isConstructor() = clsDelegate.isConstructor + + override fun getHierarchicalMethodSignature() = clsDelegate.hierarchicalMethodSignature + + override fun getDocComment() = clsDelegate.docComment + + override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean) = clsDelegate.findSuperMethodSignaturesIncludingStatic(checkAccess) + + override fun getBody() = null + + override fun isDeprecated() = clsDelegate.isDeprecated + + override fun findDeepestSuperMethod() = clsDelegate.findDeepestSuperMethod() + + override fun findDeepestSuperMethods() = clsDelegate.findDeepestSuperMethods() + + override fun findSuperMethods() = clsDelegate.findSuperMethods() + + override fun findSuperMethods(checkAccess: Boolean) = clsDelegate.findSuperMethods(checkAccess) + + override fun findSuperMethods(parentClass: PsiClass?) = clsDelegate.findSuperMethods(parentClass) + + override fun getContainingFile() = parent.containingFile + + override fun isValid() = containingClass.isValid } fun KtLightMethod.isTraitFakeOverride(): Boolean { 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 8845e8e9475..cd617625f65 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 @@ -28,34 +28,27 @@ import com.intellij.psi.impl.compiled.ClsFileImpl import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.asJava.LightClassGenerationSupport -import org.jetbrains.kotlin.asJava.builder.* +import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory +import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder import org.jetbrains.kotlin.asJava.classes.FakeLightClassForFileOfPackage import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration import org.jetbrains.kotlin.asJava.finder.JavaElementFinder -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName -import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.ClsJavaStubByVirtualFileCache -import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtLightClassForDecompiledDeclaration +import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.* import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper -import org.jetbrains.kotlin.idea.project.ResolveElementCache import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope.Companion.sourceAndClassFiles import org.jetbrains.kotlin.idea.util.ProjectRootsUtil -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException -import org.jetbrains.kotlin.resolve.lazy.ResolveSession import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.utils.sure import java.util.* @@ -64,72 +57,36 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG private val scopeFileComparator = JavaElementFinder.byClasspathComparator(GlobalSearchScope.allScope(project)) private val psiManager: PsiManager = PsiManager.getInstance(project) - override fun createLightClassDataHolderForClassOrObject(classOrObject: KtClassOrObject, build: (LightClassConstructionContext) -> LightClassBuilderResult): LightClassDataHolder { - val (stub, bindingContext, diagnostics) = build(getContextForClassOrObject(classOrObject)) - bindingContext.get(BindingContext.CLASS, classOrObject) ?: return InvalidLightClassDataHolder - - return LightClassDataHolderImpl( - stub, - diagnostics - ) - } - - fun getContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext { - if (classOrObject.isLocal) { - return getContextForLocalClassOrObject(classOrObject) + override fun createLightClassDataHolderForClassOrObject( + classOrObject: KtClassOrObject, builder: LightClassBuilder + ): LightClassDataHolder { + return if (classOrObject.isLocal) { + LazyLightClassDataHolder( + builder, + exactContextProvider = { IDELightClassContexts.contextForLocalClassOrObject(classOrObject) }, + dummyContextProvider = null + ) } else { - return getContextForNonLocalClassOrObject(classOrObject) + LazyLightClassDataHolder( + builder, + exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) }, + dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) } + ) } } - private fun getContextForNonLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext { - val resolutionFacade = classOrObject.getResolutionFacade() - val bindingContext = if (classOrObject is KtClass && classOrObject.isAnnotation()) { - // need to make sure default values for parameters are resolved - // because java resolve depends on whether there is a default value for an annotation attribute - resolutionFacade.getFrontendService(ResolveElementCache::class.java) - .resolvePrimaryConstructorParametersDefaultValues(classOrObject) - } - else { - resolutionFacade.analyze(classOrObject) - } - val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject).sure { - "Class descriptor was not found for ${classOrObject.getElementTextWithContext()}" - } - ForceResolveUtil.forceResolveAllContents(classDescriptor) - return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor) - } - private fun getContextForLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext { - val resolutionFacade = classOrObject.getResolutionFacade() - val bindingContext = resolutionFacade.analyze(classOrObject) - - val descriptor = bindingContext.get(BindingContext.CLASS, classOrObject) - - if (descriptor == null) { - LOG.warn("No class descriptor in context for class: " + classOrObject.getElementTextWithContext()) - return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor) - } - - ForceResolveUtil.forceResolveAllContents(descriptor) - - return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor) - } - - override fun createLightClassDataHolderForFacade(files: Collection, build: (LightClassConstructionContext) -> LightClassBuilderResult): LightClassDataHolder { - val (stub, _, diagnostics) = build(getContextForFacade(files)) - return LightClassDataHolderImpl(stub, diagnostics) - } - - fun getContextForFacade(files: Collection): LightClassConstructionContext { + override fun createLightClassDataHolderForFacade(files: Collection, builder: LightClassBuilder): LightClassDataHolder { assert(!files.isEmpty()) { "No files in facade" } val sortedFiles = files.sortedWith(scopeFileComparator) - val file = sortedFiles.first() - val resolveSession = file.getResolutionFacade().getFrontendService(ResolveSession::class.java) - forceResolvePackageDeclarations(files, resolveSession) - return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor) + + return LazyLightClassDataHolder( + builder, + exactContextProvider = { IDELightClassContexts.contextForFacade(sortedFiles) }, + dummyContextProvider = { IDELightClassContexts.lightContextForFacade(sortedFiles) } + ) } override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection { @@ -267,48 +224,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG } } - private fun forceResolvePackageDeclarations(files: Collection, session: ResolveSession) { - for (file in files) { - if (file.isScript) continue - - val packageFqName = file.packageFqName - - // make sure we create a package descriptor - val packageDescriptor = session.moduleDescriptor.getPackage(packageFqName) - if (packageDescriptor.isEmpty()) { - LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.name + "\n" + file.text) - session.forceResolveAll() - continue - } - - for (declaration in file.declarations) { - if (declaration is KtFunction) { - val name = declaration.nameAsSafeName - val functions = packageDescriptor.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE) - for (descriptor in functions) { - ForceResolveUtil.forceResolveAllContents(descriptor) - } - } - else if (declaration is KtProperty) { - val name = declaration.nameAsSafeName - val properties = packageDescriptor.memberScope.getContributedVariables(name, NoLookupLocation.FROM_IDE) - for (descriptor in properties) { - ForceResolveUtil.forceResolveAllContents(descriptor) - } - } - else if (declaration is KtClassOrObject || declaration is KtTypeAlias || declaration is KtDestructuringDeclaration) { - // Do nothing: we are not interested in classes or type aliases, - // and all destructuring declarations are erroneous at top level - } - else { - LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.name + "\n" + file.text) - } - } - - ForceResolveUtil.forceResolveAllContents(session.getFileAnnotations(file)) - } - } - private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: KtClassOrObject): KtLightClassForDecompiledDeclaration? { if (decompiledClassOrObject is KtEnumEntry) { return null @@ -386,10 +301,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG javaFileStub.psi = fakeFile return fakeFile.classes.single() as ClsClassImpl } - - companion object { - private val LOG = Logger.getInstance(IDELightClassGenerationSupport::class.java) - } } class KtFileClassProviderImpl(val lightClassGenerationSupport: LightClassGenerationSupport) : KtFileClassProvider { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/IDELightClassContexts.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/IDELightClassContexts.kt new file mode 100644 index 00000000000..6901202c80c --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/IDELightClassContexts.kt @@ -0,0 +1,324 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.caches.resolve.lightClasses + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analyzer.LanguageSettingsProvider +import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.container.get +import org.jetbrains.kotlin.container.useImpl +import org.jetbrains.kotlin.container.useInstance +import org.jetbrains.kotlin.context.LazyResolveToken +import org.jetbrains.kotlin.context.ModuleContext +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.frontend.di.configureModule +import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.project.IdeaEnvironment +import org.jetbrains.kotlin.idea.project.ResolveElementCache +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.calls.CallResolver +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument +import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults +import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.constants.StringValue +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform +import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl +import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil +import org.jetbrains.kotlin.resolve.lazy.ResolveSession +import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.WrappedTypeFactory +import org.jetbrains.kotlin.utils.sure + +object IDELightClassContexts { + + private val LOG = Logger.getInstance(this::class.java) + + fun contextForNonLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext { + val resolutionFacade = classOrObject.getResolutionFacade() + val bindingContext = if (classOrObject is KtClass && classOrObject.isAnnotation()) { + // need to make sure default values for parameters are resolved + // because java resolve depends on whether there is a default value for an annotation attribute + resolutionFacade.getFrontendService(ResolveElementCache::class.java) + .resolvePrimaryConstructorParametersDefaultValues(classOrObject) + } + else { + resolutionFacade.analyze(classOrObject) + } + val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject).sure { + "Class descriptor was not found for ${classOrObject.getElementTextWithContext()}" + } + ForceResolveUtil.forceResolveAllContents(classDescriptor) + return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor) + } + + fun contextForLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext { + val resolutionFacade = classOrObject.getResolutionFacade() + val bindingContext = resolutionFacade.analyze(classOrObject) + + val descriptor = bindingContext.get(BindingContext.CLASS, classOrObject) + + if (descriptor == null) { + LOG.warn("No class descriptor in context for class: " + classOrObject.getElementTextWithContext()) + return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor) + } + + ForceResolveUtil.forceResolveAllContents(descriptor) + + return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor) + } + + + fun contextForFacade(files: List): LightClassConstructionContext { + val resolveSession = files.first().getResolutionFacade().getFrontendService(ResolveSession::class.java) + + forceResolvePackageDeclarations(files, resolveSession) + + return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor) + } + + fun lightContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext { + val resolveSession = setupAdHocResolve(classOrObject.project, classOrObject.getResolutionFacade().moduleDescriptor, listOf(classOrObject.containingKtFile)) + + ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(classOrObject)) + + return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor) + } + + fun lightContextForFacade(files: List): LightClassConstructionContext { + val representativeFile = files.first() + val resolveSession = setupAdHocResolve(representativeFile.project, representativeFile.getResolutionFacade().moduleDescriptor, files) + + forceResolvePackageDeclarations(files, resolveSession) + + return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor) + } + + fun forceResolvePackageDeclarations(files: Collection, session: ResolveSession) { + for (file in files) { + if (file.isScript) continue + + val packageFqName = file.packageFqName + + // make sure we create a package descriptor + val packageDescriptor = session.moduleDescriptor.getPackage(packageFqName) + if (packageDescriptor.isEmpty()) { + LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.name + "\n" + file.text) + session.forceResolveAll() + continue + } + + for (declaration in file.declarations) { + if (declaration is KtFunction) { + val name = declaration.nameAsSafeName + val functions = packageDescriptor.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE) + for (descriptor in functions) { + ForceResolveUtil.forceResolveAllContents(descriptor) + } + } + else if (declaration is KtProperty) { + val name = declaration.nameAsSafeName + val properties = packageDescriptor.memberScope.getContributedVariables(name, NoLookupLocation.FROM_IDE) + for (descriptor in properties) { + ForceResolveUtil.forceResolveAllContents(descriptor) + } + } + else if (declaration is KtClassOrObject || declaration is KtTypeAlias || declaration is KtDestructuringDeclaration) { + // Do nothing: we are not interested in classes or type aliases, + // and all destructuring declarations are erroneous at top level + } + else { + LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.name + "\n" + file.text) + } + } + + ForceResolveUtil.forceResolveAllContents(session.getFileAnnotations(file)) + } + } + + + private fun setupAdHocResolve(project: Project, realWorldModule: ModuleDescriptor, files: List): ResolveSession { + val trace = BindingTraceContext() + val sm = LockBasedStorageManager.NO_LOCKS + val moduleDescriptor = ModuleDescriptorImpl(realWorldModule.name, sm, realWorldModule.builtIns) + + setupDependencies(moduleDescriptor, realWorldModule) + + val moduleInfo = files.first().getModuleInfo() + val container = createContainer("LightClassStub", JvmPlatform) { + val jvmTarget = LanguageSettingsProvider.getInstance(project).getTargetPlatform(moduleInfo) as? JvmTarget + configureModule( + ModuleContext(moduleDescriptor, project), JvmPlatform, + jvmTarget ?: JvmTarget.DEFAULT, trace + ) + + useInstance(GlobalSearchScope.EMPTY_SCOPE) + useInstance(LookupTracker.DO_NOTHING) + useImpl() + useInstance(LanguageSettingsProvider.getInstance(project).getLanguageVersionSettings(moduleInfo, project)) + useInstance(FileBasedDeclarationProviderFactory(sm, files)) + + useImpl() + + useInstance(object : WrappedTypeFactory(sm) { + override fun createLazyWrappedType(computation: () -> KotlinType): KotlinType = errorType() + + override fun createDeferredType(trace: BindingTrace, computation: () -> KotlinType) = errorType() + + override fun createRecursionIntolerantDeferredType(trace: BindingTrace, computation: () -> KotlinType) = errorType() + + private fun errorType() = ErrorUtils.createErrorType("Error type in ad hoc resolve for lighter classes") + }) + + IdeaEnvironment.configure(this) + useImpl() + + useImpl() + } + + + val resolveSession = container.get() + moduleDescriptor.initialize(CompositePackageFragmentProvider(listOf(resolveSession.packageFragmentProvider))) + return resolveSession + } + + private fun setupDependencies(moduleDescriptor: ModuleDescriptorImpl, realWorldModule: ModuleDescriptor) { + val jvmFieldClass = realWorldModule.getPackage(FqName("kotlin.jvm")).memberScope + .getContributedClassifier(Name.identifier("JvmField"), NoLookupLocation.FROM_IDE) + + if (jvmFieldClass != null) { + moduleDescriptor.setDependencies(moduleDescriptor, jvmFieldClass.module as ModuleDescriptorImpl, moduleDescriptor.builtIns.builtInsModule) + } + else { + moduleDescriptor.setDependencies(moduleDescriptor, moduleDescriptor.builtIns.builtInsModule) + } + } + + // see JvmPlatformAnnotations.kt, JvmFlagAnnotations.kt, also PsiModifier.MODIFIERS + private val annotationsThatAffectCodegen = listOf( + "JvmField", "JvmOverloads", "JvmName", "JvmStatic", + "Synchronized", "Transient", "Volatile", "Strictfp" + ).map { FqName("kotlin.jvm").child(Name.identifier(it)) } + + class AdHocAnnotationResolver( + private val moduleDescriptor: ModuleDescriptor, + callResolver: CallResolver, + constantExpressionEvaluator: ConstantExpressionEvaluator, + storageManager: StorageManager + ) : AnnotationResolverImpl(callResolver, constantExpressionEvaluator, storageManager) { + + override fun resolveAnnotationEntries(scope: LexicalScope, annotationEntries: List, trace: BindingTrace, shouldResolveArguments: Boolean): Annotations { + return super.resolveAnnotationEntries(scope, annotationEntries, trace, shouldResolveArguments) + } + + override fun resolveAnnotationType(scope: LexicalScope, entryElement: KtAnnotationEntry, trace: BindingTrace): KotlinType { + return annotationClassByEntry(entryElement)?.defaultType ?: super.resolveAnnotationType(scope, entryElement, trace) + } + + private fun annotationClassByEntry(entryElement: KtAnnotationEntry): ClassDescriptor? { + val annotationTypeReferencePsi = (entryElement.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: return null + val referencedName = annotationTypeReferencePsi.getReferencedName() + for (annotationFqName in annotationsThatAffectCodegen) { + if (referencedName == annotationFqName.shortName().asString()) { + moduleDescriptor.getPackage(annotationFqName.parent()).memberScope + .getContributedClassifier(annotationFqName.shortName(), NoLookupLocation.FROM_IDE)?.let { return it as? ClassDescriptor } + + } + } + return null + } + + override fun resolveAnnotationCall(annotationEntry: KtAnnotationEntry, scope: LexicalScope, trace: BindingTrace): OverloadResolutionResults { + val annotationConstructor = annotationClassByEntry(annotationEntry)?.constructors?.singleOrNull() + ?: return super.resolveAnnotationCall(annotationEntry, scope, trace) + val valueArgumentText = valueArgumentText(annotationEntry) + ?: return super.resolveAnnotationCall(annotationEntry, scope, trace) + val fakeResolvedCall = object : ResolvedCall { + override fun getStatus() = ResolutionStatus.SUCCESS + override fun getCandidateDescriptor() = annotationConstructor + override fun getResultingDescriptor() = annotationConstructor + override fun getValueArguments() = + annotationConstructor.valueParameters.singleOrNull()?.let { mapOf(it to FakeResolvedValueArgument(valueArgumentText)) }.orEmpty() + + override fun getCall() = notImplemented + override fun getExtensionReceiver() = notImplemented + override fun getDispatchReceiver() = notImplemented + override fun getExplicitReceiverKind() = notImplemented + + override fun getValueArgumentsByIndex() = notImplemented + override fun getArgumentMapping(valueArgument: ValueArgument) = notImplemented + override fun getTypeArguments() = notImplemented + override fun getDataFlowInfoForArguments() = notImplemented + override fun getSmartCastDispatchReceiverType() = notImplemented + } + + return object : OverloadResolutionResults { + override fun isSingleResult() = true + override fun getResultingCall(): ResolvedCall = fakeResolvedCall + override fun getResultingDescriptor() = annotationConstructor + override fun getAllCandidates() = notImplemented + override fun getResultingCalls() = notImplemented + override fun getResultCode() = notImplemented + override fun isSuccess() = notImplemented + override fun isNothing() = notImplemented + override fun isAmbiguity() = notImplemented + override fun isIncomplete() = notImplemented + } + } + + private fun valueArgumentText(annotationEntry: KtAnnotationEntry) = + ((annotationEntry.valueArguments.singleOrNull()?.getArgumentExpression() as? KtStringTemplateExpression)?.entries?.singleOrNull() as? KtLiteralStringTemplateEntry)?.text + + override fun getAnnotationArgumentValue(trace: BindingTrace, valueParameter: ValueParameterDescriptor, resolvedArgument: ResolvedValueArgument): ConstantValue<*>? { + if (resolvedArgument is FakeResolvedValueArgument) return StringValue(resolvedArgument.argumentText, moduleDescriptor.builtIns) + + return super.getAnnotationArgumentValue(trace, valueParameter, resolvedArgument) + } + + private class FakeResolvedValueArgument(val argumentText: String) : ResolvedValueArgument { + override fun getArguments() = notImplemented + } + } + + private val notImplemented: Nothing + get() = error("Should not be called") +} + diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/LazyLightClassDataHolder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/LazyLightClassDataHolder.kt new file mode 100644 index 00000000000..4f6ca6ff200 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/LazyLightClassDataHolder.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.caches.resolve.lightClasses + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassType +import com.intellij.psi.impl.java.stubs.PsiJavaFileStub +import org.jetbrains.kotlin.asJava.builder.* +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.elements.KtLightField +import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl +import org.jetbrains.kotlin.psi.KtClassOrObject + +typealias LightClassBuilder = (LightClassConstructionContext) -> LightClassBuilderResult +typealias LightClassContextProvider = () -> LightClassConstructionContext + +class LazyLightClassDataHolder( + builder: LightClassBuilder, + exactContextProvider: LightClassContextProvider, + dummyContextProvider: LightClassContextProvider? +) : LightClassDataHolder { + + private val exactResultLazyValue = lazy(LazyThreadSafetyMode.PUBLICATION) { builder(exactContextProvider()) } + + private val exactResult: LightClassBuilderResult by exactResultLazyValue + + private val lazyInexactResult by lazy(LazyThreadSafetyMode.PUBLICATION) { + dummyContextProvider?.let { builder.invoke(it()) } + } + + private val inexactResult: LightClassBuilderResult? + get() = if (exactResultLazyValue.isInitialized()) null else lazyInexactResult + + override val javaFileStub get() = exactResult.stub + override val extraDiagnostics get() = exactResult.diagnostics + + // for facade or defaultImpls + override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData = + LazyLightClassData(relyOnDummySupertypes = true) { lightClassBuilderResult -> + findDelegate(lightClassBuilderResult.stub) + } + + override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData = + LazyLightClassData(relyOnDummySupertypes = classOrObject.getSuperTypeList() == null) { lightClassBuilderResult -> + lightClassBuilderResult.stub.findDelegate(classOrObject) + } + + private inner class LazyLightClassData( + private val relyOnDummySupertypes: Boolean, + findDelegate: (LightClassBuilderResult) -> PsiClass + ) : LightClassData { + override val clsDelegate: PsiClass by lazy(LazyThreadSafetyMode.PUBLICATION) { findDelegate(exactResult) } + + private val dummyDelegate: PsiClass? by lazy(LazyThreadSafetyMode.PUBLICATION) { inexactResult?.let(findDelegate) } + + override fun getOwnFields(containingClass: KtLightClass): List { + if (dummyDelegate == null) return clsDelegate.fields.map { KtLightFieldImpl.fromClsField(it, containingClass) } + + return dummyDelegate!!.fields.map { dummyField -> + val memberOrigin = ClsWrapperStubPsiFactory.getMemberOrigin(dummyField)!! + val fieldName = dummyField.name!! + KtLightFieldImpl.lazy(dummyField, memberOrigin, containingClass) { + clsDelegate.findFieldByName(fieldName, false)!!.apply { + assert(this.memberIndex!! == dummyField.memberIndex!!) + } + } + } + } + + override fun getOwnMethods(containingClass: KtLightClass): List { + if (dummyDelegate == null) return clsDelegate.methods.map { KtLightMethodImpl.fromClsMethod(it, containingClass) } + + return dummyDelegate!!.methods.map { dummyMethod -> + val methodName = dummyMethod.name + KtLightMethodImpl.lazy(dummyMethod, containingClass, ClsWrapperStubPsiFactory.getMemberOrigin(dummyMethod)) { + val dummyIndex = dummyMethod.memberIndex!! + clsDelegate.findMethodsByName(methodName, false).filter { + delegateCandidate -> delegateCandidate.memberIndex == dummyIndex + }.single().apply { + assert(this.parameterList.parametersCount == dummyMethod.parameterList.parametersCount) + } + } + } + } + + override val supertypes: Array + get() = if (relyOnDummySupertypes && dummyDelegate != null) dummyDelegate!!.superTypes else clsDelegate.superTypes + + } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt index 614b1302115..5ff6c2a8463 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt @@ -20,7 +20,10 @@ import com.intellij.psi.PsiCodeBlock import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod import com.intellij.psi.PsiNameIdentifierOwner -import org.jetbrains.kotlin.asJava.elements.* +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.elements.isGetter +import org.jetbrains.kotlin.asJava.elements.isSetter import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.uast.* @@ -30,9 +33,15 @@ import org.jetbrains.uast.kotlin.* open class KotlinUMethod( psi: KtLightMethod, override val uastParent: UElement? -) : UMethod, JavaUElementWithComments, PsiMethod by psi { +) : UAnnotationMethod, JavaUElementWithComments, PsiMethod by psi { override val psi: KtLightMethod = unwrap(psi) + override val uastDefaultValue by lz { + val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null + val defaultValue = annotationParameter.defaultValue ?: return@lz null + getLanguagePlugin().convertElement(defaultValue, this) as? UExpression + } + private val kotlinOrigin = (psi.originalElement as KtLightElement<*, *>).kotlinOrigin override val annotations by lz { @@ -73,20 +82,6 @@ open class KotlinUMethod( override fun hashCode() = psi.hashCode() companion object { - fun create(psi: KtLightMethod, containingElement: UElement?) = when (psi) { - is KtLightMethodImpl.KtLightAnnotationMethod -> KotlinUAnnotationMethod(psi, containingElement) - else -> KotlinUMethod(psi, containingElement) - } - } -} - -class KotlinUAnnotationMethod( - override val psi: KtLightMethodImpl.KtLightAnnotationMethod, - containingElement: UElement? -) : KotlinUMethod(psi, containingElement), UAnnotationMethod { - override val uastDefaultValue by lz { - val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null - val defaultValue = annotationParameter.defaultValue ?: return@lz null - getLanguagePlugin().convertElement(defaultValue, this) as? UExpression + fun create(psi: KtLightMethod, containingElement: UElement?) = KotlinUMethod(psi, containingElement) } } \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/MethodReference.kt b/plugins/uast-kotlin/testData/MethodReference.kt index a927015d46c..b38c238b72e 100644 --- a/plugins/uast-kotlin/testData/MethodReference.kt +++ b/plugins/uast-kotlin/testData/MethodReference.kt @@ -6,4 +6,4 @@ class Foo { val x = Foo::bar // REF:Foo::bar -// RESULT:KtLightAnnotationMethod:bar +// RESULT:KtLightMethodImpl:bar diff --git a/plugins/uast-kotlin/testData/UnexpectedContainerException.types.txt b/plugins/uast-kotlin/testData/UnexpectedContainerException.types.txt index 3844346145d..6ada6272f88 100644 --- a/plugins/uast-kotlin/testData/UnexpectedContainerException.types.txt +++ b/plugins/uast-kotlin/testData/UnexpectedContainerException.types.txt @@ -14,7 +14,7 @@ UFile (package = ) [public abstract interface Callback {...] UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [()] : PsiType:UnsupportedOperationException UIdentifier (Identifier (UnsupportedOperationException)) [UIdentifier (Identifier (UnsupportedOperationException))] USimpleNameReferenceExpression (identifier = ) [] : PsiType:UnsupportedOperationException - UMethod (name = Model) [public fun Model() {...}] + UAnnotationMethod (name = Model) [public fun Model() {...}] UBlockExpression [{...}] UBlockExpression [{...}] : PsiType:Unit UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2)) [crashMe(Callback.java, { ...})] : PsiType:Unit