[LC] reduce memory consumption from SLC and DLC

The main change is drop `LightClassesLazyCreator`

KtLightClassForDecompiledDeclaration:
* fixed a few captured fields

^KT-56561 Fixed
This commit is contained in:
Dmitrii Gridin
2023-02-10 16:07:29 +01:00
committed by Space Team
parent a8e8fc1f11
commit 761553ea75
6 changed files with 336 additions and 184 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -18,8 +18,7 @@ import org.jetbrains.kotlin.analysis.decompiled.light.classes.origin.LightMember
import org.jetbrains.kotlin.analysis.decompiled.light.classes.origin.LightMemberOriginForCompiledMethod import org.jetbrains.kotlin.analysis.decompiled.light.classes.origin.LightMemberOriginForCompiledMethod
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache import org.jetbrains.kotlin.asJava.classes.ClassInnerStuffCache
import org.jetbrains.kotlin.asJava.classes.LightClassesLazyCreator
import org.jetbrains.kotlin.asJava.classes.getEnumEntriesPsiMethod import org.jetbrains.kotlin.asJava.classes.getEnumEntriesPsiMethod
import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.isGetEntriesMethod import org.jetbrains.kotlin.asJava.isGetEntriesMethod
@@ -29,36 +28,37 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
open class KtLightClassForDecompiledDeclaration( open class KtLightClassForDecompiledDeclaration(
clsDelegate: PsiClass, clsDelegate: PsiClass,
private val clsParent: PsiElement, clsParent: PsiElement,
private val file: KtClsFile, protected val file: KtClsFile,
kotlinOrigin: KtClassOrObject? kotlinOrigin: KtClassOrObject?
) : KtLightClassForDecompiledDeclarationBase(clsDelegate, clsParent, kotlinOrigin) { ) : KtLightClassForDecompiledDeclarationBase(clsDelegate, clsParent, kotlinOrigin) {
private val myInnersCache by lazyPub {
ClassInnerStuffCache(
/* aClass = */ this,
/* generateEnumMethods = */ true,
/* modificationTracker = */ KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker,
)
}
private val myInnersCache = KotlinClassInnerStuffCache( override fun getOwnMethods(): List<PsiMethod> = _methods
myClass = this,
dependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker),
lazyCreator = LightClassesLazyCreator(project)
)
override fun getOwnMethods(): MutableList<PsiMethod> = _methods override fun getOwnFields(): List<PsiField> = _fields
override fun getOwnFields(): MutableList<PsiField> = _fields override fun getOwnInnerClasses(): List<PsiClass> = _innerClasses
override fun getOwnInnerClasses(): MutableList<PsiClass> = _innerClasses override fun getFields(): Array<PsiField> = myInnersCache.fields
override fun getFields() = myInnersCache.fields override fun getMethods(): Array<PsiMethod> = myInnersCache.methods
override fun getMethods() = myInnersCache.methods override fun getConstructors(): Array<PsiMethod> = myInnersCache.constructors
override fun getConstructors() = myInnersCache.constructors override fun getInnerClasses(): Array<PsiClass> = myInnersCache.innerClasses
override fun getInnerClasses() = myInnersCache.innerClasses override fun findFieldByName(name: String, checkBases: Boolean): PsiField? = myInnersCache.findFieldByName(name, checkBases)
override fun findFieldByName(name: String, checkBases: Boolean) = myInnersCache.findFieldByName(name, checkBases) override fun findMethodsByName(name: String, checkBases: Boolean): Array<PsiMethod> = myInnersCache.findMethodsByName(name, checkBases)
override fun findMethodsByName(name: String, checkBases: Boolean) = myInnersCache.findMethodsByName(name, checkBases) override fun findInnerClassByName(name: String, checkBases: Boolean): PsiClass? = myInnersCache.findInnerClassByName(name, checkBases)
override fun findInnerClassByName(name: String, checkBases: Boolean) = myInnersCache.findInnerClassByName(name, checkBases)
override fun hasModifierProperty(name: String): Boolean = override fun hasModifierProperty(name: String): Boolean =
clsDelegate.hasModifierProperty(name) clsDelegate.hasModifierProperty(name)
@@ -82,15 +82,15 @@ open class KtLightClassForDecompiledDeclaration(
override fun getContainingClass(): PsiClass? = parent as? PsiClass override fun getContainingClass(): PsiClass? = parent as? PsiClass
override fun isInheritorDeep(baseClass: PsiClass?, classToByPass: PsiClass?): Boolean = clsDelegate.isInheritorDeep(baseClass, classToByPass) override fun isInheritorDeep(baseClass: PsiClass?, classToByPass: PsiClass?): Boolean =
clsDelegate.isInheritorDeep(baseClass, classToByPass)
override fun getAllMethodsAndTheirSubstitutors(): List<Pair<PsiMethod?, PsiSubstitutor?>?> = override fun getAllMethodsAndTheirSubstitutors(): List<Pair<PsiMethod?, PsiSubstitutor?>?> =
PsiClassImplUtil.getAllWithSubstitutorsByMap<PsiMethod>(this, PsiClassImplUtil.MemberType.METHOD) PsiClassImplUtil.getAllWithSubstitutorsByMap<PsiMethod>(this, PsiClassImplUtil.MemberType.METHOD)
override fun isInterface(): Boolean = clsDelegate.isInterface override fun isInterface(): Boolean = clsDelegate.isInterface
override fun getTypeParameters(): Array<PsiTypeParameter> = override fun getTypeParameters(): Array<PsiTypeParameter> = clsDelegate.typeParameters
clsDelegate.typeParameters
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean = override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean =
clsDelegate.isInheritor(baseClass, checkDeep) clsDelegate.isInheritor(baseClass, checkDeep)
@@ -159,59 +159,54 @@ open class KtLightClassForDecompiledDeclaration(
override fun getAllFields(): Array<PsiField> = PsiClassImplUtil.getAllFields(this) override fun getAllFields(): Array<PsiField> = PsiClassImplUtil.getAllFields(this)
private val _methods: MutableList<PsiMethod> by lazyPub { private val _methods: List<PsiMethod> by lazyPub {
mutableListOf<PsiMethod>().also { val isEnum = isEnum
val isEnum = isEnum this.clsDelegate.methods.mapNotNull { psiMethod ->
clsDelegate.methods.mapNotNullTo(it) { psiMethod -> if (isSyntheticValuesOrValueOfMethod(psiMethod)) return@mapNotNull null
if (isSyntheticValuesOrValueOfMethod(psiMethod)) return@mapNotNullTo null if (isEnum && isGetEntriesMethod(psiMethod)) {
if (isEnum && isGetEntriesMethod(psiMethod)) { return@mapNotNull getEnumEntriesPsiMethod(this)
return@mapNotNullTo getEnumEntriesPsiMethod(this) }
}
KtLightMethodForDecompiledDeclaration( KtLightMethodForDecompiledDeclaration(
funDelegate = psiMethod, funDelegate = psiMethod,
funParent = this, funParent = this,
lightMemberOrigin = LightMemberOriginForCompiledMethod(psiMethod, file) lightMemberOrigin = LightMemberOriginForCompiledMethod(psiMethod, file)
)
}
}
private val _fields: List<PsiField> by lazyPub {
this.clsDelegate.fields.map { psiField ->
if (psiField !is PsiEnumConstant) {
KtLightFieldForDecompiledDeclaration(
fldDelegate = psiField,
fldParent = this,
lightMemberOrigin = LightMemberOriginForCompiledField(psiField, file)
)
} else {
KtLightEnumEntryForDecompiledDeclaration(
fldDelegate = psiField,
fldParent = this,
lightMemberOrigin = LightMemberOriginForCompiledField(psiField, file),
file = file
) )
} }
} }
} }
private val _fields: MutableList<PsiField> by lazyPub { private val _innerClasses: List<PsiClass> by lazyPub {
mutableListOf<PsiField>().also { this.clsDelegate.innerClasses.map { psiClass ->
clsDelegate.fields.mapTo(it) { psiField -> val innerDeclaration = this.kotlinOrigin
if (psiField !is PsiEnumConstant) { ?.declarations
KtLightFieldForDecompiledDeclaration( ?.filterIsInstance<KtClassOrObject>()
fldDelegate = psiField, ?.firstOrNull { cls -> cls.name == this.clsDelegate.name }
fldParent = this,
lightMemberOrigin = LightMemberOriginForCompiledField(psiField, file)
)
} else {
KtLightEnumEntryForDecompiledDeclaration(
fldDelegate = psiField,
fldParent = this,
lightMemberOrigin = LightMemberOriginForCompiledField(psiField, file),
file = file
)
}
}
}
}
private val _innerClasses: MutableList<PsiClass> by lazyPub { KtLightClassForDecompiledDeclaration(
mutableListOf<PsiClass>().also { clsDelegate = psiClass,
clsDelegate.innerClasses.mapTo(it) { psiClass -> clsParent = this,
val innerDeclaration = kotlinOrigin file = file,
?.declarations kotlinOrigin = innerDeclaration,
?.filterIsInstance<KtClassOrObject>() )
?.firstOrNull { cls -> cls.name == clsDelegate.name }
KtLightClassForDecompiledDeclaration(
clsDelegate = psiClass,
clsParent = this,
file = file,
kotlinOrigin = innerDeclaration,
)
}
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -23,8 +23,6 @@ internal class KtLightClassForDecompiledFacade(
override val files: Collection<KtFile>, override val files: Collection<KtFile>,
) : KtLightClassForDecompiledDeclaration(clsDelegate, clsParent, file, kotlinOrigin), KtLightClassForFacade { ) : KtLightClassForDecompiledDeclaration(clsDelegate, clsParent, file, kotlinOrigin), KtLightClassForFacade {
override fun getName(): String = super<KtLightClassForFacade>.getName() override fun getName(): String = super<KtLightClassForFacade>.getName()
override val facadeClassFqName: FqName get() = file.javaFileFacadeFqName
override val facadeClassFqName: FqName = file.javaFileFacadeFqName
override val originKind: LightClassOriginKind get() = LightClassOriginKind.BINARY override val originKind: LightClassOriginKind get() = LightClassOriginKind.BINARY
} }
@@ -1,4 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. /*
* Copyright 2010-2023 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.analysis.decompiled.light.classes package org.jetbrains.kotlin.analysis.decompiled.light.classes
@@ -12,14 +15,12 @@ internal class KtLightEnumClassForDecompiledDeclaration(
clsParent: KtLightClassForDecompiledDeclaration, clsParent: KtLightClassForDecompiledDeclaration,
file: KtClsFile, file: KtClsFile,
kotlinOrigin: KtClassOrObject? kotlinOrigin: KtClassOrObject?
) : ) : KtLightClassForDecompiledDeclaration(
KtLightClassForDecompiledDeclaration( clsDelegate = psiConstantInitializer,
clsDelegate = psiConstantInitializer, clsParent = clsParent,
clsParent = clsParent, file = file,
file = file, kotlinOrigin = kotlinOrigin
kotlinOrigin = kotlinOrigin ), PsiEnumConstantInitializer {
), PsiEnumConstantInitializer {
override fun getBaseClassType(): PsiClassType = psiConstantInitializer.baseClassType override fun getBaseClassType(): PsiClassType = psiConstantInitializer.baseClassType
override fun getArgumentList(): PsiExpressionList? = psiConstantInitializer.argumentList override fun getArgumentList(): PsiExpressionList? = psiConstantInitializer.argumentList
@@ -6,21 +6,22 @@
package org.jetbrains.kotlin.asJava.classes; package org.jetbrains.kotlin.asJava.classes;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*; import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.PsiSuperMethodImplUtil; import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.impl.light.*; import com.intellij.psi.impl.light.*;
import com.intellij.psi.impl.source.PsiExtensibleClass;
import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.MethodSignature; import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException; import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ConcurrentFactoryMap;
@@ -30,6 +31,11 @@ import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin;
import org.jetbrains.kotlin.asJava.elements.KtLightMethod;
import org.jetbrains.kotlin.asJava.elements.KtLightParameter;
import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtParameter;
import java.util.*; import java.util.*;
@@ -41,31 +47,65 @@ import static com.intellij.util.ObjectUtils.notNull;
* @see com.intellij.psi.impl.source.ClassInnerStuffCache * @see com.intellij.psi.impl.source.ClassInnerStuffCache
*/ */
public final class ClassInnerStuffCache { public final class ClassInnerStuffCache {
private final PsiExtensibleClass myClass; public static final String NOT_NULL_ANNOTATION_QUALIFIER = "@" + NotNull.class.getName();
private final Ref<Pair<Long, Interner<PsiMember>>> myInterner = Ref.create();
public ClassInnerStuffCache(@NotNull PsiExtensibleClass aClass) { private final @NotNull KtExtensibleLightClass myClass;
private final @NotNull ModificationTracker myModificationTracker;
private final @NotNull Ref<Pair<Long, Interner<PsiMember>>> myInterner = Ref.create();
private final boolean myGenerateEnumMethods;
public ClassInnerStuffCache(
@NotNull KtExtensibleLightClass aClass,
boolean generateEnumMethods,
@NotNull ModificationTracker modificationTracker
) {
myGenerateEnumMethods = generateEnumMethods;
myClass = aClass; myClass = aClass;
myModificationTracker = modificationTracker;
} }
public PsiMethod @NotNull [] getConstructors() { @NotNull
return copy(CachedValuesManager.getProjectPsiDependentCache(myClass, PsiImplUtil::getConstructors)); public PsiMethod[] getConstructors() {
return copy(CachedValuesManager.getCachedValue(
myClass,
() -> CachedValueProvider.Result.createSingleDependency(
PsiImplUtil.getConstructors(myClass),
myModificationTracker
)
));
} }
public PsiField @NotNull [] getFields() { @NotNull
return copy(CachedValuesManager.getProjectPsiDependentCache(myClass, __ -> calcFields())); public PsiField[] getFields() {
return copy(CachedValuesManager.getCachedValue(
myClass,
() -> CachedValueProvider.Result.createSingleDependency(
calcFields(),
myModificationTracker
)
));
} }
public PsiMethod @NotNull [] getMethods() { @NotNull
return copy(CachedValuesManager.getProjectPsiDependentCache(myClass, __ -> calcMethods())); public PsiMethod[] getMethods() {
return copy(CachedValuesManager.getCachedValue(
myClass,
() -> CachedValueProvider.Result.createSingleDependency(
calcMethods(),
myModificationTracker
)
));
} }
public PsiClass @NotNull [] getInnerClasses() { @NotNull
return copy(CachedValuesManager.getProjectPsiDependentCache(myClass, __ -> calcInnerClasses())); public PsiClass[] getInnerClasses() {
} return copy(CachedValuesManager.getCachedValue(
myClass,
public PsiRecordComponent @NotNull [] getRecordComponents() { () -> CachedValueProvider.Result.createSingleDependency(
return copy(CachedValuesManager.getProjectPsiDependentCache(myClass, __ -> calcRecordComponents())); calcInnerClasses(),
myModificationTracker
)
));
} }
@Nullable @Nullable
@@ -74,16 +114,29 @@ public final class ClassInnerStuffCache {
return PsiClassImplUtil.findFieldByName(myClass, name, true); return PsiClassImplUtil.findFieldByName(myClass, name, true);
} }
else { else {
return CachedValuesManager.getProjectPsiDependentCache(myClass, __ -> getFieldsMap()).get(name); return CachedValuesManager.getCachedValue(
myClass,
() -> CachedValueProvider.Result.createSingleDependency(
getFieldsMap(),
myModificationTracker
)
).get(name);
} }
} }
public PsiMethod @NotNull [] findMethodsByName(String name, boolean checkBases) { @NotNull
public PsiMethod[] findMethodsByName(String name, boolean checkBases) {
if (checkBases) { if (checkBases) {
return PsiClassImplUtil.findMethodsByName(myClass, name, true); return PsiClassImplUtil.findMethodsByName(myClass, name, true);
} }
else { else {
return copy(notNull(CachedValuesManager.getProjectPsiDependentCache(myClass, __ -> getMethodsMap()).get(name), PsiMethod.EMPTY_ARRAY)); return copy(notNull(CachedValuesManager.getCachedValue(
myClass,
() -> CachedValueProvider.Result.createSingleDependency(
getMethodsMap(),
myModificationTracker
)
).get(name), PsiMethod.EMPTY_ARRAY));
} }
} }
@@ -93,37 +146,48 @@ public final class ClassInnerStuffCache {
return PsiClassImplUtil.findInnerByName(myClass, name, true); return PsiClassImplUtil.findInnerByName(myClass, name, true);
} }
else { else {
return CachedValuesManager.getProjectPsiDependentCache(myClass, __ -> getInnerClassesMap()).get(name); return CachedValuesManager.getCachedValue(
myClass,
() -> CachedValueProvider.Result.createSingleDependency(
getInnerClassesMap(),
myModificationTracker
)
).get(name);
} }
} }
@Nullable @Nullable
PsiMethod getValuesMethod() { private PsiMethod getValuesMethod() {
return myClass.isEnum() && !isAnonymousClass() && !classNameIsSealed() return isEnum() ? internMember(CachedValuesManager.getCachedValue(
? internMember(CachedValuesManager.getProjectPsiDependentCache(myClass, ClassInnerStuffCache::makeValuesMethod)) myClass,
: null; () -> CachedValueProvider.Result.createSingleDependency(
} makeValuesMethod(myClass),
myModificationTracker
private boolean classNameIsSealed() { )
return PsiKeyword.SEALED.equals(myClass.getName()) && PsiUtil.getLanguageLevel(myClass).isAtLeast(LanguageLevel.JDK_17); )) : null;
} }
@Nullable @Nullable
private PsiMethod getValueOfMethod() { private PsiMethod getValueOfMethod() {
return myClass.isEnum() && !isAnonymousClass() return isEnum() ? internMember(CachedValuesManager.getCachedValue(
? internMember(CachedValuesManager.getProjectPsiDependentCache(myClass, ClassInnerStuffCache::makeValueOfMethod)) myClass,
: null; () -> CachedValueProvider.Result.createSingleDependency(
makeValueOfMethod(myClass),
myModificationTracker
)
)) : null;
} }
private boolean isAnonymousClass() { private boolean isEnum() {
return myClass.getName() == null || myClass instanceof PsiAnonymousClass; return myGenerateEnumMethods && myClass.isEnum();
} }
private static <T> T[] copy(T[] value) { private static <T> T[] copy(T[] value) {
return value.length == 0 ? value : value.clone(); return value.length == 0 ? value : value.clone();
} }
private PsiField @NotNull [] calcFields() { @NotNull
private PsiField[] calcFields() {
List<PsiField> own = myClass.getOwnFields(); List<PsiField> own = myClass.getOwnFields();
List<PsiField> ext = internMembers(PsiAugmentProvider.collectAugments(myClass, PsiField.class, null)); List<PsiField> ext = internMembers(PsiAugmentProvider.collectAugments(myClass, PsiField.class, null));
return ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY); return ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY);
@@ -134,23 +198,25 @@ public final class ClassInnerStuffCache {
return ContainerUtil.map(members, this::internMember); return ContainerUtil.map(members, this::internMember);
} }
@SuppressWarnings("unchecked")
private <T extends PsiMember> T internMember(T m) { private <T extends PsiMember> T internMember(T m) {
if (m == null) return null; if (m == null) return null;
long modCount = myClass.getManager().getModificationTracker().getModificationCount(); long modCount = myModificationTracker.getModificationCount();
synchronized (myInterner) { synchronized (myInterner) {
Pair<Long, Interner<PsiMember>> pair = myInterner.get(); Pair<Long, Interner<PsiMember>> pair = myInterner.get();
if (pair == null || pair.first.longValue() != modCount) { if (pair == null || pair.first.longValue() != modCount) {
myInterner.set(pair = Pair.create(modCount, Interner.createWeakInterner())); myInterner.set(pair = Pair.create(modCount, Interner.createWeakInterner()));
} }
//noinspection unchecked
return (T) pair.second.intern(m); return (T) pair.second.intern(m);
} }
} }
private PsiMethod @NotNull [] calcMethods() { @NotNull
private PsiMethod[] calcMethods() {
List<PsiMethod> own = myClass.getOwnMethods(); List<PsiMethod> own = myClass.getOwnMethods();
List<PsiMethod> ext = internMembers(PsiAugmentProvider.collectAugments(myClass, PsiMethod.class, null)); List<PsiMethod> ext = internMembers(PsiAugmentProvider.collectAugments(myClass, PsiMethod.class, null));
if (myGenerateEnumMethods && myClass.isEnum()) { if (isEnum()) {
ext = new ArrayList<>(ext); ext = new ArrayList<>(ext);
ContainerUtil.addIfNotNull(ext, getValuesMethod()); ContainerUtil.addIfNotNull(ext, getValuesMethod());
ContainerUtil.addIfNotNull(ext, getValueOfMethod()); ContainerUtil.addIfNotNull(ext, getValueOfMethod());
@@ -158,26 +224,21 @@ public final class ClassInnerStuffCache {
return ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY); return ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY);
} }
private PsiClass @NotNull [] calcInnerClasses() { @NotNull
private PsiClass[] calcInnerClasses() {
List<PsiClass> own = myClass.getOwnInnerClasses(); List<PsiClass> own = myClass.getOwnInnerClasses();
List<PsiClass> ext = internMembers(PsiAugmentProvider.collectAugments(myClass, PsiClass.class, null)); List<PsiClass> ext = internMembers(PsiAugmentProvider.collectAugments(myClass, PsiClass.class, null));
return ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY); return ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY);
} }
private PsiRecordComponent @NotNull [] calcRecordComponents() {
PsiRecordHeader header = myClass.getRecordHeader();
return header == null ? PsiRecordComponent.EMPTY_ARRAY : header.getRecordComponents();
}
@NotNull @NotNull
private Map<String, PsiField> getFieldsMap() { private Map<String, PsiField> getFieldsMap() {
Map<String, PsiField> cachedFields = new HashMap<>(); Map<String, PsiField> cachedFields = new HashMap<>();
for (PsiField field : myClass.getOwnFields()) { for (PsiField field : myClass.getOwnFields()) {
String name = field.getName(); String name = field.getName();
if (!cachedFields.containsKey(name)) { cachedFields.putIfAbsent(name, field);
cachedFields.put(name, field);
}
} }
return ConcurrentFactoryMap.createMap(name -> { return ConcurrentFactoryMap.createMap(name -> {
PsiField result = cachedFields.get(name); PsiField result = cachedFields.get(name);
return result != null ? result : return result != null ? result :
@@ -188,14 +249,13 @@ public final class ClassInnerStuffCache {
@NotNull @NotNull
private Map<String, PsiMethod[]> getMethodsMap() { private Map<String, PsiMethod[]> getMethodsMap() {
List<PsiMethod> ownMethods = myClass.getOwnMethods(); List<PsiMethod> ownMethods = myClass.getOwnMethods();
return ConcurrentFactoryMap.createMap(name -> { return ConcurrentFactoryMap.createMap(name -> JBIterable
return JBIterable .from(ownMethods)
.from(ownMethods).filter(m -> name.equals(m.getName())) .filter(m -> name.equals(m.getName()))
.append("values".equals(name) ? getValuesMethod() : null) .append("values".equals(name) ? getValuesMethod() : null)
.append("valueOf".equals(name) ? getValueOfMethod() : null) .append("valueOf".equals(name) ? getValueOfMethod() : null)
.append(internMembers(PsiAugmentProvider.collectAugments(myClass, PsiMethod.class, name))) .append(internMembers(PsiAugmentProvider.collectAugments(myClass, PsiMethod.class, name)))
.toArray(PsiMethod.EMPTY_ARRAY); .toArray(PsiMethod.EMPTY_ARRAY));
});
} }
@NotNull @NotNull
@@ -210,18 +270,20 @@ public final class ClassInnerStuffCache {
cachedInners.put(name, psiClass); cachedInners.put(name, psiClass);
} }
} }
return ConcurrentFactoryMap.createMap(name -> { return ConcurrentFactoryMap.createMap(name -> {
PsiClass result = cachedInners.get(name); PsiClass result = cachedInners.get(name);
return result != null ? result : return result != null
internMember(ContainerUtil.getFirstItem(PsiAugmentProvider.collectAugments(myClass, PsiClass.class, name))); ? result
: internMember(ContainerUtil.getFirstItem(PsiAugmentProvider.collectAugments(myClass, PsiClass.class, name)));
}); });
} }
private static PsiMethod makeValuesMethod(PsiExtensibleClass enumClass) { private static PsiMethod makeValuesMethod(KtExtensibleLightClass enumClass) {
return new EnumSyntheticMethod(enumClass, EnumMethodKind.Values); return new EnumSyntheticMethod(enumClass, EnumMethodKind.Values);
} }
private static PsiMethod makeValueOfMethod(PsiExtensibleClass enumClass) { private static PsiMethod makeValueOfMethod(KtExtensibleLightClass enumClass) {
return new EnumSyntheticMethod(enumClass, EnumMethodKind.ValueOf); return new EnumSyntheticMethod(enumClass, EnumMethodKind.ValueOf);
} }
@@ -230,14 +292,14 @@ public final class ClassInnerStuffCache {
Values, Values,
} }
private static class EnumSyntheticMethod extends LightElement implements PsiMethod, SyntheticElement { private static class EnumSyntheticMethod extends LightElement implements PsiMethod, SyntheticElement, KtLightMethod {
private final PsiClass myClass; private final KtExtensibleLightClass myClass;
private final EnumMethodKind myKind; private final EnumMethodKind myKind;
private final PsiType myReturnType; private final PsiType myReturnType;
private final LightParameterListBuilder myParameterList; private final LightParameterListBuilder myParameterList;
private final LightModifierList myModifierList; private final LightModifierList myModifierList;
EnumSyntheticMethod(@NotNull PsiClass enumClass, EnumMethodKind kind) { EnumSyntheticMethod(@NotNull KtExtensibleLightClass enumClass, EnumMethodKind kind) {
super(enumClass.getManager(), enumClass.getLanguage()); super(enumClass.getManager(), enumClass.getLanguage());
myClass = enumClass; myClass = enumClass;
myKind = kind; myKind = kind;
@@ -256,6 +318,12 @@ public final class ClassInnerStuffCache {
} }
} }
private @NotNull PsiAnnotation[] createNotNullAnnotation() {
return new PsiAnnotation[] {
PsiElementFactory.getInstance(getProject()).createAnnotationFromText(NOT_NULL_ANNOTATION_QUALIFIER, myClass)
};
}
private @NotNull PsiType createReturnType() { private @NotNull PsiType createReturnType() {
PsiClassType type = JavaPsiFacade.getElementFactory(getProject()).createType(myClass); PsiClassType type = JavaPsiFacade.getElementFactory(getProject()).createType(myClass);
if (myKind == EnumMethodKind.Values) { if (myKind == EnumMethodKind.Values) {
@@ -267,6 +335,20 @@ public final class ClassInnerStuffCache {
@NotNull @NotNull
private LightModifierList createModifierList() { private LightModifierList createModifierList() {
return new LightModifierList(myManager, getLanguage(), PsiModifier.PUBLIC, PsiModifier.STATIC) { return new LightModifierList(myManager, getLanguage(), PsiModifier.PUBLIC, PsiModifier.STATIC) {
private final PsiAnnotation[] notNullAnnotations = createNotNullAnnotation();
@Override
@NotNull
public PsiAnnotation[] getAnnotations() {
return ClassInnerStuffCache.copy(notNullAnnotations);
}
@Override
public PsiAnnotation findAnnotation(@NotNull String qualifiedName) {
PsiAnnotation notNullAnnotation = notNullAnnotations[0];
return qualifiedName == notNullAnnotation.getQualifiedName() ? notNullAnnotation : null;
}
@Override @Override
public PsiElement getParent() { public PsiElement getParent() {
return EnumSyntheticMethod.this; return EnumSyntheticMethod.this;
@@ -279,12 +361,54 @@ public final class ClassInnerStuffCache {
LightParameterListBuilder parameters = new LightParameterListBuilder(myManager, getLanguage()); LightParameterListBuilder parameters = new LightParameterListBuilder(myManager, getLanguage());
if (myKind == EnumMethodKind.ValueOf) { if (myKind == EnumMethodKind.ValueOf) {
PsiClassType string = PsiType.getJavaLangString(myManager, GlobalSearchScope.allScope(getProject())); PsiClassType string = PsiType.getJavaLangString(myManager, GlobalSearchScope.allScope(getProject()));
LightParameter parameter = new LightParameter("name", string, this, getLanguage(), false); LightParameter parameter = new MyKtLightParameter(string, this);
parameters.addParameter(parameter); parameters.addParameter(parameter);
} }
return parameters; return parameters;
} }
private static final class MyKtLightParameter extends LightParameter implements KtLightParameter {
private final @NotNull KtLightMethod myMethod;
MyKtLightParameter(@NotNull PsiType type, @NotNull KtLightMethod declarationScope) {
super("name", type, declarationScope, declarationScope.getLanguage(), false);
myMethod = declarationScope;
}
@Nullable
@Override
public KtParameter getKotlinOrigin() {
return null;
}
@NotNull
@Override
public KtLightMethod getMethod() {
return myMethod;
}
@Override
public PsiElement getParent() {
return myMethod;
}
@Override
public PsiFile getContainingFile() {
return myMethod.getContainingFile();
}
@Override
public String getText() {
return getName();
}
@Override
public TextRange getTextRange() {
return TextRange.EMPTY_RANGE;
}
}
@Override @Override
public int getTextOffset() { public int getTextOffset() {
return myClass.getTextOffset(); return myClass.getTextOffset();
@@ -292,7 +416,17 @@ public final class ClassInnerStuffCache {
@Override @Override
public String toString() { public String toString() {
return null; return myClass.getText();
}
@Override
public String getText() {
return "";
}
@Override
public TextRange getTextRange() {
return TextRange.EMPTY_RANGE;
} }
@Override @Override
@@ -319,7 +453,7 @@ public final class ClassInnerStuffCache {
} }
@Override @Override
public @Nullable PsiClass getContainingClass() { public @NotNull KtExtensibleLightClass getContainingClass() {
return myClass; return myClass;
} }
@@ -340,9 +474,15 @@ public final class ClassInnerStuffCache {
@Override @Override
public @NotNull PsiReferenceList getThrowsList() { public @NotNull PsiReferenceList getThrowsList() {
LightReferenceListBuilder throwsList = new LightReferenceListBuilder(myManager, getLanguage(), PsiReferenceList.Role.THROWS_LIST); LightReferenceListBuilder throwsList = new LightReferenceListBuilder(
myManager,
getLanguage(),
PsiReferenceList.Role.THROWS_LIST
);
if (myKind == EnumMethodKind.ValueOf) { if (myKind == EnumMethodKind.ValueOf) {
throwsList.addReference("java.lang.IllegalArgumentException"); throwsList.addReference(IllegalArgumentException.class.getName());
throwsList.addReference(NullPointerException.class.getName());
} }
return throwsList; return throwsList;
@@ -382,17 +522,20 @@ public final class ClassInnerStuffCache {
} }
@Override @Override
public PsiMethod @NotNull [] findSuperMethods() { @NotNull
public PsiMethod[] findSuperMethods() {
return PsiSuperMethodImplUtil.findSuperMethods(this); return PsiSuperMethodImplUtil.findSuperMethods(this);
} }
@Override @Override
public PsiMethod @NotNull [] findSuperMethods(boolean checkAccess) { @NotNull
public PsiMethod[] findSuperMethods(boolean checkAccess) {
return PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess); return PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess);
} }
@Override @Override
public PsiMethod @NotNull [] findSuperMethods(PsiClass parentClass) { @NotNull
public PsiMethod[] findSuperMethods(PsiClass parentClass) {
return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass); return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass);
} }
@@ -402,12 +545,14 @@ public final class ClassInnerStuffCache {
} }
@Override @Override
@Deprecated
public @Nullable PsiMethod findDeepestSuperMethod() { public @Nullable PsiMethod findDeepestSuperMethod() {
return PsiSuperMethodImplUtil.findDeepestSuperMethod(this); return PsiSuperMethodImplUtil.findDeepestSuperMethod(this);
} }
@Override @Override
public PsiMethod @NotNull [] findDeepestSuperMethods() { @NotNull
public PsiMethod[] findDeepestSuperMethods() {
return PsiMethod.EMPTY_ARRAY; return PsiMethod.EMPTY_ARRAY;
} }
@@ -431,6 +576,11 @@ public final class ClassInnerStuffCache {
return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this); return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this);
} }
@Override
public @Nullable PsiAnnotationMemberValue getDefaultValue() {
return null;
}
@Override @Override
public boolean hasTypeParameters() { public boolean hasTypeParameters() {
return false; return false;
@@ -447,8 +597,26 @@ public final class ClassInnerStuffCache {
} }
@Override @Override
public PsiTypeParameter @NotNull [] getTypeParameters() { @NotNull
public PsiTypeParameter[] getTypeParameters() {
return PsiTypeParameter.EMPTY_ARRAY; return PsiTypeParameter.EMPTY_ARRAY;
} }
@Override
public boolean isMangled() {
return false;
}
@Nullable
@Override
public KtDeclaration getKotlinOrigin() {
return null;
}
@Nullable
@Override
public LightMemberOrigin getLightMemberOrigin() {
return null;
}
} }
} }
@@ -14,11 +14,12 @@ import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.impl.light.* import com.intellij.psi.impl.light.*
import com.intellij.psi.javadoc.PsiDocComment import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.* import com.intellij.psi.util.MethodSignature
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod
import com.intellij.psi.util.PsiUtil
import com.intellij.util.ArrayUtil import com.intellij.util.ArrayUtil
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import gnu.trove.THashMap import gnu.trove.THashMap
import org.jetbrains.annotations.NotNull
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.KtLightParameter import org.jetbrains.kotlin.asJava.elements.KtLightParameter
@@ -288,7 +289,10 @@ private class KotlinEnumSyntheticMethod(
private companion object { private companion object {
private fun makeNotNullAnnotation(context: PsiClass): PsiAnnotation { private fun makeNotNullAnnotation(context: PsiClass): PsiAnnotation {
return PsiElementFactory.getInstance(context.project).createAnnotationFromText("@" + NotNull::class.java.name, context) return PsiElementFactory.getInstance(context.project).createAnnotationFromText(
ClassInnerStuffCache.NOT_NULL_ANNOTATION_QUALIFIER,
context,
)
} }
} }
} }
@@ -7,15 +7,16 @@ package org.jetbrains.kotlin.light.classes.symbol.classes
import com.intellij.navigation.ItemPresentation import com.intellij.navigation.ItemPresentation
import com.intellij.navigation.ItemPresentationProviders import com.intellij.navigation.ItemPresentationProviders
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Pair import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.* import com.intellij.psi.impl.InheritanceImplUtil
import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.PsiImplUtil
import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.impl.light.LightElement import com.intellij.psi.impl.light.LightElement
import com.intellij.psi.javadoc.PsiDocComment import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.scope.PsiScopeProcessor import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.PsiUtil import com.intellij.psi.util.PsiUtil
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtModule
@@ -33,30 +34,15 @@ import javax.swing.Icon
abstract class SymbolLightClassBase protected constructor(val ktModule: KtModule, manager: PsiManager) : abstract class SymbolLightClassBase protected constructor(val ktModule: KtModule, manager: PsiManager) :
LightElement(manager, KotlinLanguage.INSTANCE), PsiClass, KtExtensibleLightClass { LightElement(manager, KotlinLanguage.INSTANCE), PsiClass, KtExtensibleLightClass {
private class SymbolLightClassesLazyCreator(private val project: Project) : KotlinClassInnerStuffCache.LazyCreator() {
override fun <T : Any> get(initializer: () -> T, dependencies: List<Any>): Lazy<T> = object : Lazy<T> {
private val cachedValue = PsiCachedValueImpl(PsiManager.getInstance(project)) {
CachedValueProvider.Result.create(initializer(), dependencies)
}
override val value: T get() = cachedValue.value ?: error("Unexpected null value from PsiCachedValueImpl") private val myInnersCache by lazyPub {
ClassInnerStuffCache(
override fun isInitialized(): Boolean { /* aClass = */ this,
// Lazy is a bad interface here as it has unneeded and unused in LC `isInitialized` method /* generateEnumMethods = */ false,
// considering Interface Segregation Principle, Lazy should be repaced with a simpler interface with only `value` method /* modificationTracker = */ project.createProjectWideOutOfBlockModificationTracker(),
error("Should not be called for LC") )
}
}
} }
@Suppress("LeakingThis")
private val myInnersCache = KotlinClassInnerStuffCache(
myClass = this@SymbolLightClassBase,
dependencies = listOf(manager.project.createProjectWideOutOfBlockModificationTracker()),
lazyCreator = SymbolLightClassesLazyCreator(project),
generateEnumMethods = false,
)
override fun getFields(): Array<PsiField> = myInnersCache.fields override fun getFields(): Array<PsiField> = myInnersCache.fields
override fun getMethods(): Array<PsiMethod> = myInnersCache.methods override fun getMethods(): Array<PsiMethod> = myInnersCache.methods