J2K IDELightClassGenerationSupport: autoconvert

This commit is contained in:
Pavel V. Talanov
2015-09-07 15:14:09 +03:00
parent c37b15d629
commit d1d18618b5
@@ -14,514 +14,418 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.caches.resolve; package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.ide.highlighter.JavaClassFileType; import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.libraries.LibraryUtil; import com.intellij.openapi.roots.libraries.LibraryUtil
import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ClassFileViewProvider; import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager; import com.intellij.psi.PsiManager
import com.intellij.psi.impl.compiled.ClsClassImpl; import com.intellij.psi.impl.compiled.ClsClassImpl
import com.intellij.psi.impl.compiled.ClsFileImpl; import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.PsiClassHolderFileStub; import com.intellij.psi.stubs.PsiClassHolderFileStub
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.cls.ClsFormatException; import com.intellij.util.cls.ClsFormatException
import kotlin.KotlinPackage; import kotlin.KotlinPackage
import kotlin.jvm.functions.Function1; import org.jetbrains.kotlin.asJava.*
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.decompiler.navigation.JetSourceNavigationHelper
import org.jetbrains.kotlin.asJava.*; import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.idea.stubindex.JetFullClassNameIndex
import org.jetbrains.kotlin.idea.decompiler.navigation.JetSourceNavigationHelper; import org.jetbrains.kotlin.idea.stubindex.JetTopLevelClassByPackageIndex
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade; import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
import org.jetbrains.kotlin.idea.stubindex.JetFullClassNameIndex; import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil
import org.jetbrains.kotlin.idea.stubindex.JetTopLevelClassByPackageIndex; import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil; import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil; import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.lazy.*
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.lazy.*;
import org.jetbrains.kotlin.resolve.scopes.JetScope;
import java.io.IOException; import java.io.IOException
import java.util.*; import java.util.*
import static kotlin.KotlinPackage.*; import kotlin.KotlinPackage.*
import static org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourceAndClassFiles; import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourceAndClassFiles
import static org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourcesAndLibraries; import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourcesAndLibraries
public class IDELightClassGenerationSupport extends LightClassGenerationSupport { public class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() {
private static final Logger LOG = Logger.getInstance(IDELightClassGenerationSupport.class); private val scopeFileComparator: Comparator<PsiElement>
private val psiManager: PsiManager
private final Project project; init {
this.scopeFileComparator = JavaElementFinder.byClasspathComparator(GlobalSearchScope.allScope(project))
private final Comparator<PsiElement> scopeFileComparator; this.psiManager = PsiManager.getInstance(project)
private final PsiManager psiManager;
public IDELightClassGenerationSupport(@NotNull Project project) {
this.project = project;
this.scopeFileComparator = JavaElementFinder.byClasspathComparator(GlobalSearchScope.allScope(project));
this.psiManager = PsiManager.getInstance(project);
} }
@NotNull override fun getContextForPackage(files: Collection<JetFile>): LightClassConstructionContext {
@Override assert(!files.isEmpty(), "No files in package")
public LightClassConstructionContext getContextForPackage(@NotNull Collection<JetFile> files) {
assert !files.isEmpty() : "No files in package";
return getContextForFiles(files); return getContextForFiles(files)
} }
@NotNull private fun getContextForFiles(files: Collection<JetFile>): LightClassConstructionContext {
private LightClassConstructionContext getContextForFiles(@NotNull Collection<JetFile> files) { val sortedFiles = ArrayList(files)
List<JetFile> sortedFiles = new ArrayList<JetFile>(files); Collections.sort(sortedFiles, scopeFileComparator)
Collections.sort(sortedFiles, scopeFileComparator);
JetFile file = sortedFiles.get(0); val file = sortedFiles.get(0)
ResolveSession resolveSession = ResolvePackage.getResolutionFacade(file).getFrontendService(ResolveSession.class); val resolveSession = file.getResolutionFacade().getFrontendService(javaClass<ResolveSession>())
forceResolvePackageDeclarations(files, resolveSession); forceResolvePackageDeclarations(files, resolveSession)
return new LightClassConstructionContext(resolveSession.getBindingContext(), resolveSession.getModuleDescriptor()); return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.getModuleDescriptor())
} }
@NotNull override fun getContextForClassOrObject(classOrObject: JetClassOrObject): LightClassConstructionContext {
@Override val resolutionFacade = classOrObject.getResolutionFacade()
public LightClassConstructionContext getContextForClassOrObject(@NotNull JetClassOrObject classOrObject) {
ResolutionFacade resolutionFacade = ResolvePackage.getResolutionFacade(classOrObject);
ModuleDescriptor moduleDescriptor = resolutionFacade.getModuleDescriptor(); val moduleDescriptor = resolutionFacade.moduleDescriptor
BindingContext bindingContext = resolutionFacade.analyze(classOrObject, BodyResolveMode.FULL); val bindingContext = resolutionFacade.analyze(classOrObject, BodyResolveMode.FULL)
if (classOrObject.isLocal()) { if (classOrObject.isLocal()) {
ClassDescriptor descriptor = bindingContext.get(BindingContext.CLASS, classOrObject); val descriptor = bindingContext.get(BindingContext.CLASS, classOrObject)
if (descriptor == null) { if (descriptor == null) {
LOG.warn("No class descriptor in context for class: " + PsiUtilPackage.getElementTextWithContext(classOrObject)); LOG.warn("No class descriptor in context for class: " + classOrObject.getElementTextWithContext())
return new LightClassConstructionContext(bindingContext, moduleDescriptor); return LightClassConstructionContext(bindingContext, moduleDescriptor)
} }
ForceResolveUtil.forceResolveAllContents(descriptor); ForceResolveUtil.forceResolveAllContents<ClassDescriptor>(descriptor)
return new LightClassConstructionContext(bindingContext, moduleDescriptor); return LightClassConstructionContext(bindingContext, moduleDescriptor)
} }
ForceResolveUtil.forceResolveAllContents(resolutionFacade.resolveToDescriptor(classOrObject)); ForceResolveUtil.forceResolveAllContents(resolutionFacade.resolveToDescriptor(classOrObject))
return new LightClassConstructionContext(bindingContext, moduleDescriptor); return LightClassConstructionContext(bindingContext, moduleDescriptor)
} }
@NotNull override fun getContextForFacade(files: Collection<JetFile>): LightClassConstructionContext {
@Override assert(!files.isEmpty(), "No files in facade")
public LightClassConstructionContext getContextForFacade(@NotNull Collection<JetFile> files) {
assert !files.isEmpty() : "No files in facade";
return getContextForFiles(files); return getContextForFiles(files)
} }
private static void forceResolvePackageDeclarations(@NotNull Collection<JetFile> files, @NotNull KotlinCodeAnalyzer session) { override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<JetClassOrObject> {
for (JetFile file : files) { return JetFullClassNameIndex.getInstance().get(fqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project))
}
override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<JetFile> {
return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSourceAndClassFiles(searchScope, project), project)
}
override fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName, searchScope: GlobalSearchScope): Collection<JetClassOrObject> {
return JetTopLevelClassByPackageIndex.getInstance().get(
packageFqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project))
}
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
return PackageIndexUtil.packageExists(fqName, kotlinSourceAndClassFiles(scope, project), project)
}
override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
return PackageIndexUtil.getSubPackageFqNames(fqn, kotlinSourceAndClassFiles(scope, project), project, JetScope.ALL_NAME_FILTER)
}
override fun getPsiClass(classOrObject: JetClassOrObject): PsiClass? {
val virtualFile = classOrObject.containingFile.virtualFile
if (virtualFile != null && LibraryUtil.findLibraryEntry(virtualFile, classOrObject.project) != null) {
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile)) {
return getLightClassForDecompiledClassOrObject(classOrObject)
}
return JetSourceNavigationHelper.getOriginalClass(classOrObject)
}
return KotlinLightClassForExplicitDeclaration.create(psiManager, classOrObject)
}
override fun getPackageClasses(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val result = ArrayList<PsiClass>()
val packageClassesInfos = findPackageClassesInfos(packageFqName, scope)
for (info in packageClassesInfos) {
val files = PackagePartClassUtils.getFilesWithCallables(info.files)
if (files.isEmpty()) continue
val moduleInfo = info.moduleInfo
if (moduleInfo is ModuleSourceInfo) {
result.addAll(getLightClassesForPackageFacadeWithSources(packageFqName, files, moduleInfo))
}
else {
result.addAll(getLightClassesForDecompiledFacadeFiles(files))
}
}
return result
}
private fun getLightClassesForPackageFacadeWithSources(
packageFqName: FqName,
facadeFiles: List<JetFile>,
moduleInfo: IdeaModuleInfo): List<PsiClass> {
val lightClassForFacade = KotlinLightClassForFacade.createForPackageFacade(psiManager, packageFqName, moduleInfo.contentScope(), facadeFiles)
return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles)
}
private fun getLightClassesForFacadeWithFiles(
lightClassForFacade: KotlinLightClassForFacade?,
facadeFiles: List<JetFile>): List<PsiClass> {
if (lightClassForFacade == null) return emptyList()
val lightClasses = ArrayList<PsiClass>()
lightClasses.add(lightClassForFacade)
if (facadeFiles.size() > 1) {
for (file in facadeFiles) {
lightClasses.add(FakeLightClassForFileOfPackage(psiManager, lightClassForFacade, file))
}
}
return lightClasses
}
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val result = ArrayList<PsiClass>()
val facadeClassesInfos = findFacadeClassesInfos(facadeFqName, scope)
for (info in facadeClassesInfos) {
val files = PackagePartClassUtils.getFilesWithCallables(info.files)
if (files.isEmpty()) continue
val moduleInfo = info.moduleInfo
if (moduleInfo is ModuleSourceInfo) {
result.addAll(getLightClassesForStaticFacadeWithSources(facadeFqName, files, moduleInfo))
}
else {
result.addAll(getLightClassesForDecompiledFacadeFiles(files))
}
}
return result
}
private fun getLightClassesForStaticFacadeWithSources(
facadeFqName: FqName,
facadeFiles: List<JetFile>,
moduleInfo: IdeaModuleInfo): List<PsiClass> {
val lightClassForFacade = KotlinLightClassForFacade.createForFacade(
psiManager, facadeFqName, moduleInfo.contentScope(), facadeFiles)
return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles)
}
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<JetFile> {
return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourcesAndLibraries(scope, project), project)
}
private fun findFacadeClassesInfos(facadeFqName: FqName, scope: GlobalSearchScope): List<KotlinLightFacadeClassInfo> {
val facadeFiles = findFilesForFacade(facadeFqName, scope)
return groupFilesIntoFacadeLightClassesByModuleInfo(facadeFiles)
}
override fun resolveClassToDescriptor(classOrObject: JetClassOrObject): ClassDescriptor? {
try {
return classOrObject.resolveToDescriptor() as ClassDescriptor
}
catch (e: NoDescriptorForDeclarationException) {
return null
}
}
private fun findPackageClassesInfos(
fqName: FqName, wholeScope: GlobalSearchScope): List<KotlinLightFacadeClassInfo> {
val allFiles = findFilesForPackage(fqName, wholeScope)
return groupFilesIntoFacadeLightClassesByModuleInfo(allFiles)
}
private class KotlinLightFacadeClassInfo(public val files: Collection<JetFile>, public val moduleInfo: IdeaModuleInfo)
private class CachedJavaStub(public var modificationStamp: Long, public var javaFileStub: PsiJavaFileStubImpl)
companion object {
private val LOG = Logger.getInstance(javaClass<IDELightClassGenerationSupport>())
private fun forceResolvePackageDeclarations(files: Collection<JetFile>, session: KotlinCodeAnalyzer) {
for (file in files) {
// SCRIPT: not supported // SCRIPT: not supported
if (file.isScript()) continue; if (file.isScript) continue
FqName packageFqName = file.getPackageFqName(); val packageFqName = file.packageFqName
// make sure we create a package descriptor // make sure we create a package descriptor
PackageViewDescriptor packageDescriptor = session.getModuleDescriptor().getPackage(packageFqName); val packageDescriptor = session.moduleDescriptor.getPackage(packageFqName)
if (packageDescriptor.isEmpty()) { if (packageDescriptor.isEmpty()) {
LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.getName() + "\n" + file.getText()); LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.name + "\n" + file.text)
session.forceResolveAll(); session.forceResolveAll()
continue; continue
} }
for (JetDeclaration declaration : file.getDeclarations()) { for (declaration in file.declarations) {
if (declaration instanceof JetFunction) { if (declaration is JetFunction) {
JetFunction jetFunction = (JetFunction) declaration; val name = declaration.nameAsSafeName
Name name = jetFunction.getNameAsSafeName(); val functions = packageDescriptor.memberScope.getFunctions(name, NoLookupLocation.FROM_IDE)
Collection<FunctionDescriptor> functions = packageDescriptor.getMemberScope().getFunctions(name, NoLookupLocation.FROM_IDE); for (descriptor in functions) {
for (FunctionDescriptor descriptor : functions) { ForceResolveUtil.forceResolveAllContents(descriptor)
ForceResolveUtil.forceResolveAllContents(descriptor);
} }
} }
else if (declaration instanceof JetProperty) { else if (declaration is JetProperty) {
JetProperty jetProperty = (JetProperty) declaration; val name = declaration.nameAsSafeName
Name name = jetProperty.getNameAsSafeName(); val properties = packageDescriptor.memberScope.getProperties(name, NoLookupLocation.FROM_IDE)
Collection<VariableDescriptor> properties = packageDescriptor.getMemberScope().getProperties(name, NoLookupLocation.FROM_IDE); for (descriptor in properties) {
for (VariableDescriptor descriptor : properties) { ForceResolveUtil.forceResolveAllContents(descriptor)
ForceResolveUtil.forceResolveAllContents(descriptor);
} }
} }
else if (declaration instanceof JetClassOrObject) { else if (declaration is JetClassOrObject) {
// Do nothing: we are not interested in classes // Do nothing: we are not interested in classes
} }
else { else {
LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.getName() + "\n" + file.getText()); LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.name + "\n" + file.text)
} }
} }
} }
} }
@NotNull private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: JetClassOrObject): PsiClass? {
@Override if (decompiledClassOrObject is JetEnumEntry) {
public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) { return null
return JetFullClassNameIndex.getInstance().get(fqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project)); }
val containingJetFile = decompiledClassOrObject.getContainingJetFile()
if (!containingJetFile.isCompiled) {
return null
}
val rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingJetFile) ?: return null
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile)
} }
@NotNull private fun findCorrespondingLightClass(
@Override decompiledClassOrObject: JetClassOrObject,
public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) { rootLightClassForDecompiledFile: PsiClass): PsiClass {
return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSourceAndClassFiles(searchScope, project), project); val relativeFqName = getClassRelativeName(decompiledClassOrObject)
} val iterator = relativeFqName.pathSegments().iterator()
val base = iterator.next()
@NotNull assert(rootLightClassForDecompiledFile.name == base.asString()) { "Light class for file:\n" + decompiledClassOrObject.getContainingJetFile().virtualFile.canonicalPath + "\nwas expected to have name: " + base.asString() + "\n Actual: " + rootLightClassForDecompiledFile.name }
@Override var current = rootLightClassForDecompiledFile
public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
@NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
) {
return JetTopLevelClassByPackageIndex.getInstance().get(
packageFqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project)
);
}
@Override
public boolean packageExists(@NotNull FqName fqName, @NotNull GlobalSearchScope scope) {
return PackageIndexUtil.packageExists(fqName, kotlinSourceAndClassFiles(scope, project), project);
}
@NotNull
@Override
public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
return PackageIndexUtil.getSubPackageFqNames(fqn, kotlinSourceAndClassFiles(scope, project), project, JetScope.ALL_NAME_FILTER);
}
@Nullable
@Override
public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) {
VirtualFile virtualFile = classOrObject.getContainingFile().getVirtualFile();
if (virtualFile != null && LibraryUtil.findLibraryEntry(virtualFile, classOrObject.getProject()) != null) {
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile)) {
return getLightClassForDecompiledClassOrObject(classOrObject);
}
return JetSourceNavigationHelper.getOriginalClass(classOrObject);
}
return KotlinLightClassForExplicitDeclaration.Companion.create(psiManager, classOrObject);
}
@Nullable
private static PsiClass getLightClassForDecompiledClassOrObject(@NotNull JetClassOrObject decompiledClassOrObject) {
if (decompiledClassOrObject instanceof JetEnumEntry) {
return null;
}
JetFile containingJetFile = decompiledClassOrObject.getContainingJetFile();
if (!containingJetFile.isCompiled()) {
return null;
}
PsiClass rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingJetFile);
if (rootLightClassForDecompiledFile == null) return null;
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile);
}
@NotNull
private static PsiClass findCorrespondingLightClass(
@NotNull JetClassOrObject decompiledClassOrObject,
@NotNull PsiClass rootLightClassForDecompiledFile
) {
FqName relativeFqName = getClassRelativeName(decompiledClassOrObject);
Iterator<Name> iterator = relativeFqName.pathSegments().iterator();
Name base = iterator.next();
assert rootLightClassForDecompiledFile.getName().equals(base.asString())
: "Light class for file:\n" + decompiledClassOrObject.getContainingJetFile().getVirtualFile().getCanonicalPath()
+ "\nwas expected to have name: " + base.asString() + "\n Actual: " + rootLightClassForDecompiledFile.getName();
PsiClass current = rootLightClassForDecompiledFile;
while (iterator.hasNext()) { while (iterator.hasNext()) {
Name name = iterator.next(); val name = iterator.next()
PsiClass innerClass = current.findInnerClassByName(name.asString(), false); val innerClass = current.findInnerClassByName(name.asString(), false)
assert innerClass != null : "Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.getFqName() + "\n" + assert(innerClass != null) { "Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.fqName + "\n" + "File: " + decompiledClassOrObject.getContainingJetFile().virtualFile.name }
"File: " + decompiledClassOrObject.getContainingJetFile().getVirtualFile().getName(); current = innerClass
current = innerClass;
} }
return current; return current
} }
@NotNull private fun getClassRelativeName(decompiledClassOrObject: JetClassOrObject): FqName {
private static FqName getClassRelativeName(@NotNull JetClassOrObject decompiledClassOrObject) { val name = decompiledClassOrObject.nameAsName
Name name = decompiledClassOrObject.getNameAsName(); assert(name != null)
assert name != null; val parent = PsiTreeUtil.getParentOfType(decompiledClassOrObject, javaClass<JetClassOrObject>(), true)
JetClassOrObject parent = PsiTreeUtil.getParentOfType(decompiledClassOrObject, JetClassOrObject.class, true);
if (parent == null) { if (parent == null) {
assert decompiledClassOrObject.isTopLevel(); assert(decompiledClassOrObject.isTopLevel())
return FqName.topLevel(name); return FqName.topLevel(name)
} }
return getClassRelativeName(parent).child(name); return getClassRelativeName(parent).child(name)
} }
@NotNull private fun groupFilesIntoFacadeLightClassesByModuleInfo(facadeFiles: Collection<JetFile>): List<KotlinLightFacadeClassInfo> {
@Override val filesByInfo = KotlinPackage.groupByTo(
public Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) {
List<PsiClass> result = new ArrayList<PsiClass>();
List<KotlinLightFacadeClassInfo> packageClassesInfos = findPackageClassesInfos(packageFqName, scope);
for (KotlinLightFacadeClassInfo info : packageClassesInfos) {
List<JetFile> files = PackagePartClassUtils.getFilesWithCallables(info.getFiles());
if (files.isEmpty()) continue;
IdeaModuleInfo moduleInfo = info.getModuleInfo();
if (moduleInfo instanceof ModuleSourceInfo) {
result.addAll(getLightClassesForPackageFacadeWithSources(packageFqName, files, moduleInfo));
}
else {
result.addAll(getLightClassesForDecompiledFacadeFiles(files));
}
}
return result;
}
@NotNull
private List<PsiClass> getLightClassesForPackageFacadeWithSources(
@NotNull FqName packageFqName,
@NotNull List<JetFile> facadeFiles,
@NotNull IdeaModuleInfo moduleInfo
) {
KotlinLightClassForFacade lightClassForFacade =
KotlinLightClassForFacade.Factory.createForPackageFacade(psiManager, packageFqName, moduleInfo.contentScope(), facadeFiles);
return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles);
}
@NotNull
private List<PsiClass> getLightClassesForFacadeWithFiles(
@Nullable KotlinLightClassForFacade lightClassForFacade,
@NotNull List<JetFile> facadeFiles
) {
if (lightClassForFacade == null) return emptyList();
List<PsiClass> lightClasses = new ArrayList<PsiClass>();
lightClasses.add(lightClassForFacade);
if (facadeFiles.size() > 1) {
for (JetFile file : facadeFiles) {
lightClasses.add(new FakeLightClassForFileOfPackage(psiManager, lightClassForFacade, file));
}
}
return lightClasses;
}
@NotNull
@Override
public Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
List<PsiClass> result = new ArrayList<PsiClass>();
List<KotlinLightFacadeClassInfo> facadeClassesInfos = findFacadeClassesInfos(facadeFqName, scope);
for (KotlinLightFacadeClassInfo info : facadeClassesInfos) {
List<JetFile> files = PackagePartClassUtils.getFilesWithCallables(info.getFiles());
if (files.isEmpty()) continue;
IdeaModuleInfo moduleInfo = info.getModuleInfo();
if (moduleInfo instanceof ModuleSourceInfo) {
result.addAll(getLightClassesForStaticFacadeWithSources(facadeFqName, files, moduleInfo));
}
else {
result.addAll(getLightClassesForDecompiledFacadeFiles(files));
}
}
return result;
}
private List<PsiClass> getLightClassesForStaticFacadeWithSources(
@NotNull FqName facadeFqName,
@NotNull List<JetFile> facadeFiles,
@NotNull IdeaModuleInfo moduleInfo
) {
KotlinLightClassForFacade lightClassForFacade = KotlinLightClassForFacade.Factory.createForFacade(
psiManager, facadeFqName, moduleInfo.contentScope(), facadeFiles);
return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles);
}
@NotNull
@Override
public Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourcesAndLibraries(scope, project), project);
}
@NotNull
private List<KotlinLightFacadeClassInfo> findFacadeClassesInfos(FqName facadeFqName, GlobalSearchScope scope) {
Collection<JetFile> facadeFiles = findFilesForFacade(facadeFqName, scope);
return groupFilesIntoFacadeLightClassesByModuleInfo(facadeFiles);
}
@NotNull
private static List<KotlinLightFacadeClassInfo> groupFilesIntoFacadeLightClassesByModuleInfo(Collection<JetFile> facadeFiles) {
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = KotlinPackage.groupByTo(
facadeFiles, facadeFiles,
new LinkedHashMap<IdeaModuleInfo, List<JetFile>>(), LinkedHashMap<IdeaModuleInfo, List<JetFile>>(),
new Function1<JetFile, IdeaModuleInfo>() { object : Function1<JetFile, IdeaModuleInfo> {
@Override override fun invoke(file: JetFile): IdeaModuleInfo {
public IdeaModuleInfo invoke(JetFile file) { return file.getModuleInfo()
return ResolvePackage.getModuleInfo(file);
} }
}); })
List<KotlinLightFacadeClassInfo> result = new ArrayList<KotlinLightFacadeClassInfo>(); val result = ArrayList<KotlinLightFacadeClassInfo>()
for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) { for (entry in filesByInfo.entrySet()) {
result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey())); result.add(KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey()))
} }
return result; return result
} }
@Nullable private fun getLightClassesForDecompiledFacadeFiles(filesWithCallables: List<JetFile>): List<PsiClass> {
@Override val lightClasses = ArrayList<PsiClass>()
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) { for (file in filesWithCallables) {
try { if (file.isCompiled) {
return (ClassDescriptor) ResolvePackage.resolveToDescriptor(classOrObject); lightClasses.add(createLightClassForDecompiledKotlinFile(file))
} }
catch (NoDescriptorForDeclarationException e) {
return null;
} }
return lightClasses
} }
@NotNull private fun createLightClassForDecompiledKotlinFile(file: JetFile): KotlinLightClassForDecompiledDeclaration? {
private static List<PsiClass> getLightClassesForDecompiledFacadeFiles(@NotNull List<JetFile> filesWithCallables) { val virtualFile = file.virtualFile ?: return null
List<PsiClass> lightClasses = new ArrayList<PsiClass>();
for (JetFile file : filesWithCallables) { val classOrObject = singleOrNull<JetClassOrObject>(filterIsInstance<JetClassOrObject>(file.declarations, javaClass<JetClassOrObject>()))
if (file.isCompiled()) {
lightClasses.add(createLightClassForDecompiledKotlinFile(file)); val javaClsClass = createClsJavaClassFromVirtualFile(file, virtualFile, classOrObject) ?: return null
} return KotlinLightClassForDecompiledDeclaration(javaClsClass, classOrObject)
}
return lightClasses;
} }
@NotNull private fun createClsJavaClassFromVirtualFile(
private List<KotlinLightFacadeClassInfo> findPackageClassesInfos( decompiledKotlinFile: JetFile,
@NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope virtualFile: VirtualFile,
) { decompiledClassOrObject: JetClassOrObject?): ClsClassImpl? {
Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope); val javaFileStub = getOrCreateJavaFileStub(decompiledKotlinFile, virtualFile) ?: return null
return groupFilesIntoFacadeLightClassesByModuleInfo(allFiles); val manager = PsiManager.getInstance(decompiledKotlinFile.project)
} val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
override fun getNavigationElement(): PsiElement {
private static final class KotlinLightFacadeClassInfo {
private final Collection<JetFile> files;
private final IdeaModuleInfo moduleInfo;
public KotlinLightFacadeClassInfo(@NotNull Collection<JetFile> files, @NotNull IdeaModuleInfo moduleInfo) {
this.files = files;
this.moduleInfo = moduleInfo;
}
@NotNull
public Collection<JetFile> getFiles() {
return files;
}
@NotNull
public IdeaModuleInfo getModuleInfo() {
return moduleInfo;
}
}
@Nullable
private static KotlinLightClassForDecompiledDeclaration createLightClassForDecompiledKotlinFile(@NotNull JetFile file) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) {
return null;
}
JetClassOrObject classOrObject = singleOrNull(filterIsInstance(file.getDeclarations(), JetClassOrObject.class));
ClsClassImpl javaClsClass = createClsJavaClassFromVirtualFile(file, virtualFile, classOrObject);
if (javaClsClass == null) {
return null;
}
return new KotlinLightClassForDecompiledDeclaration(javaClsClass, classOrObject);
}
@Nullable
private static ClsClassImpl createClsJavaClassFromVirtualFile(
@NotNull final JetFile decompiledKotlinFile,
@NotNull VirtualFile virtualFile,
@Nullable final JetClassOrObject decompiledClassOrObject
) {
final PsiJavaFileStubImpl javaFileStub = getOrCreateJavaFileStub(decompiledKotlinFile, virtualFile);
if (javaFileStub == null) {
return null;
}
PsiManager manager = PsiManager.getInstance(decompiledKotlinFile.getProject());
ClsFileImpl fakeFile = new ClsFileImpl(new ClassFileViewProvider(manager, virtualFile)) {
@NotNull
@Override
public PsiElement getNavigationElement() {
if (decompiledClassOrObject != null) { if (decompiledClassOrObject != null) {
return decompiledClassOrObject.getNavigationElement().getContainingFile(); return decompiledClassOrObject.navigationElement.containingFile
} }
return super.getNavigationElement(); return super.getNavigationElement()
} }
@NotNull override fun getStub(): PsiClassHolderFileStub<PsiFile> {
@Override return javaFileStub
public PsiClassHolderFileStub getStub() {
return javaFileStub;
} }
@Override override fun getMirror(): PsiElement {
public PsiElement getMirror() { return decompiledKotlinFile
return decompiledKotlinFile;
} }
}; }
fakeFile.setPhysical(false); fakeFile.isPhysical = false
javaFileStub.setPsi(fakeFile); javaFileStub.setPsi(fakeFile)
return (ClsClassImpl) single(fakeFile.getClasses()); return single<PsiClass>(fakeFile.classes) as ClsClassImpl
} }
private final static Key<CachedJavaStub> cachedJavaStubKey = Key.create("CACHED_JAVA_STUB"); private val cachedJavaStubKey = Key.create<CachedJavaStub>("CACHED_JAVA_STUB")
private static class CachedJavaStub { private fun getOrCreateJavaFileStub(
public CachedJavaStub(long modificationStamp, @NotNull PsiJavaFileStubImpl javaFileStub) { decompiledKotlinFile: JetFile,
this.modificationStamp = modificationStamp; virtualFile: VirtualFile): PsiJavaFileStubImpl? {
this.javaFileStub = javaFileStub; val cachedJavaStub = decompiledKotlinFile.getUserData(cachedJavaStubKey)
} val fileModificationStamp = virtualFile.modificationStamp
public long modificationStamp;
public PsiJavaFileStubImpl javaFileStub;
}
@Nullable
private static PsiJavaFileStubImpl getOrCreateJavaFileStub(
@NotNull JetFile decompiledKotlinFile,
@NotNull VirtualFile virtualFile) {
CachedJavaStub cachedJavaStub = decompiledKotlinFile.getUserData(cachedJavaStubKey);
long fileModificationStamp = virtualFile.getModificationStamp();
if (cachedJavaStub != null && cachedJavaStub.modificationStamp == fileModificationStamp) { if (cachedJavaStub != null && cachedJavaStub.modificationStamp == fileModificationStamp) {
return cachedJavaStub.javaFileStub; return cachedJavaStub.javaFileStub
} }
PsiJavaFileStubImpl stub = (PsiJavaFileStubImpl) createStub(virtualFile); val stub = createStub(virtualFile) as PsiJavaFileStubImpl?
if (stub != null) { if (stub != null) {
decompiledKotlinFile.putUserData(cachedJavaStubKey, new CachedJavaStub(fileModificationStamp, stub)); decompiledKotlinFile.putUserData(cachedJavaStubKey, CachedJavaStub(fileModificationStamp, stub))
} }
return stub; return stub
} }
@Nullable private fun createStub(file: VirtualFile): PsiJavaFileStub? {
private static PsiJavaFileStub createStub(@NotNull VirtualFile file) { if (file.fileType !== JavaClassFileType.INSTANCE) return null
if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
try { try {
return ClsFileImpl.buildFileStub(file, file.contentsToByteArray()); return ClsFileImpl.buildFileStub(file, file.contentsToByteArray())
} }
catch (ClsFormatException e) { catch (e: ClsFormatException) {
LOG.debug(e); LOG.debug(e)
} }
catch (IOException e) { catch (e: IOException) {
LOG.debug(e); LOG.debug(e)
}
LOG.error("Failed to build java cls class for " + file.canonicalPath!!)
return null
} }
LOG.error("Failed to build java cls class for " + file.getCanonicalPath());
return null;
} }
} }