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))
// SCRIPT: not supported
if (file.isScript()) continue;
FqName packageFqName = file.getPackageFqName();
// make sure we create a package descriptor
PackageViewDescriptor packageDescriptor = session.getModuleDescriptor().getPackage(packageFqName);
if (packageDescriptor.isEmpty()) {
LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.getName() + "\n" + file.getText());
session.forceResolveAll();
continue;
}
for (JetDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof JetFunction) {
JetFunction jetFunction = (JetFunction) declaration;
Name name = jetFunction.getNameAsSafeName();
Collection<FunctionDescriptor> functions = packageDescriptor.getMemberScope().getFunctions(name, NoLookupLocation.FROM_IDE);
for (FunctionDescriptor descriptor : functions) {
ForceResolveUtil.forceResolveAllContents(descriptor);
}
}
else if (declaration instanceof JetProperty) {
JetProperty jetProperty = (JetProperty) declaration;
Name name = jetProperty.getNameAsSafeName();
Collection<VariableDescriptor> properties = packageDescriptor.getMemberScope().getProperties(name, NoLookupLocation.FROM_IDE);
for (VariableDescriptor descriptor : properties) {
ForceResolveUtil.forceResolveAllContents(descriptor);
}
}
else if (declaration instanceof JetClassOrObject) {
// Do nothing: we are not interested in classes
}
else {
LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.getName() + "\n" + file.getText());
}
}
}
} }
@NotNull override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<JetFile> {
@Override return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSourceAndClassFiles(searchScope, project), project)
public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) {
return JetFullClassNameIndex.getInstance().get(fqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project));
} }
@NotNull override fun findClassOrObjectDeclarationsInPackage(
@Override packageFqName: FqName, searchScope: GlobalSearchScope): Collection<JetClassOrObject> {
public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) {
return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSourceAndClassFiles(searchScope, project), project);
}
@NotNull
@Override
public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
@NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
) {
return JetTopLevelClassByPackageIndex.getInstance().get( return JetTopLevelClassByPackageIndex.getInstance().get(
packageFqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project) packageFqName.asString(), project, kotlinSourceAndClassFiles(searchScope, project))
);
} }
@Override override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
public boolean packageExists(@NotNull FqName fqName, @NotNull GlobalSearchScope scope) { return PackageIndexUtil.packageExists(fqName, kotlinSourceAndClassFiles(scope, project), project)
return PackageIndexUtil.packageExists(fqName, kotlinSourceAndClassFiles(scope, project), project);
} }
@NotNull override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
@Override return PackageIndexUtil.getSubPackageFqNames(fqn, kotlinSourceAndClassFiles(scope, project), project, JetScope.ALL_NAME_FILTER)
public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
return PackageIndexUtil.getSubPackageFqNames(fqn, kotlinSourceAndClassFiles(scope, project), project, JetScope.ALL_NAME_FILTER);
} }
@Nullable override fun getPsiClass(classOrObject: JetClassOrObject): PsiClass? {
@Override val virtualFile = classOrObject.containingFile.virtualFile
public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) { if (virtualFile != null && LibraryUtil.findLibraryEntry(virtualFile, classOrObject.project) != null) {
VirtualFile virtualFile = classOrObject.getContainingFile().getVirtualFile();
if (virtualFile != null && LibraryUtil.findLibraryEntry(virtualFile, classOrObject.getProject()) != null) {
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile)) { if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile)) {
return getLightClassForDecompiledClassOrObject(classOrObject); return getLightClassForDecompiledClassOrObject(classOrObject)
} }
return JetSourceNavigationHelper.getOriginalClass(classOrObject); return JetSourceNavigationHelper.getOriginalClass(classOrObject)
} }
return KotlinLightClassForExplicitDeclaration.Companion.create(psiManager, classOrObject); return KotlinLightClassForExplicitDeclaration.create(psiManager, classOrObject)
} }
@Nullable override fun getPackageClasses(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
private static PsiClass getLightClassForDecompiledClassOrObject(@NotNull JetClassOrObject decompiledClassOrObject) { val result = ArrayList<PsiClass>()
if (decompiledClassOrObject instanceof JetEnumEntry) { val packageClassesInfos = findPackageClassesInfos(packageFqName, scope)
return null; for (info in packageClassesInfos) {
} val files = PackagePartClassUtils.getFilesWithCallables(info.files)
JetFile containingJetFile = decompiledClassOrObject.getContainingJetFile(); if (files.isEmpty()) continue
if (!containingJetFile.isCompiled()) {
return null;
}
PsiClass rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingJetFile);
if (rootLightClassForDecompiledFile == null) return null;
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile); val moduleInfo = info.moduleInfo
} if (moduleInfo is ModuleSourceInfo) {
result.addAll(getLightClassesForPackageFacadeWithSources(packageFqName, files, moduleInfo))
@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()) {
Name name = iterator.next();
PsiClass innerClass = current.findInnerClassByName(name.asString(), false);
assert innerClass != null : "Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.getFqName() + "\n" +
"File: " + decompiledClassOrObject.getContainingJetFile().getVirtualFile().getName();
current = innerClass;
}
return current;
}
@NotNull
private static FqName getClassRelativeName(@NotNull JetClassOrObject decompiledClassOrObject) {
Name name = decompiledClassOrObject.getNameAsName();
assert name != null;
JetClassOrObject parent = PsiTreeUtil.getParentOfType(decompiledClassOrObject, JetClassOrObject.class, true);
if (parent == null) {
assert decompiledClassOrObject.isTopLevel();
return FqName.topLevel(name);
}
return getClassRelativeName(parent).child(name);
}
@NotNull
@Override
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 { else {
result.addAll(getLightClassesForDecompiledFacadeFiles(files)); result.addAll(getLightClassesForDecompiledFacadeFiles(files))
} }
} }
return result; return result
} }
@NotNull private fun getLightClassesForPackageFacadeWithSources(
private List<PsiClass> getLightClassesForPackageFacadeWithSources( packageFqName: FqName,
@NotNull FqName packageFqName, facadeFiles: List<JetFile>,
@NotNull List<JetFile> facadeFiles, moduleInfo: IdeaModuleInfo): List<PsiClass> {
@NotNull IdeaModuleInfo moduleInfo val lightClassForFacade = KotlinLightClassForFacade.createForPackageFacade(psiManager, packageFqName, moduleInfo.contentScope(), facadeFiles)
) { return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles)
KotlinLightClassForFacade lightClassForFacade =
KotlinLightClassForFacade.Factory.createForPackageFacade(psiManager, packageFqName, moduleInfo.contentScope(), facadeFiles);
return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles);
} }
@NotNull private fun getLightClassesForFacadeWithFiles(
private List<PsiClass> getLightClassesForFacadeWithFiles( lightClassForFacade: KotlinLightClassForFacade?,
@Nullable KotlinLightClassForFacade lightClassForFacade, facadeFiles: List<JetFile>): List<PsiClass> {
@NotNull List<JetFile> facadeFiles if (lightClassForFacade == null) return emptyList()
) {
if (lightClassForFacade == null) return emptyList();
List<PsiClass> lightClasses = new ArrayList<PsiClass>(); val lightClasses = ArrayList<PsiClass>()
lightClasses.add(lightClassForFacade); lightClasses.add(lightClassForFacade)
if (facadeFiles.size() > 1) { if (facadeFiles.size() > 1) {
for (JetFile file : facadeFiles) { for (file in facadeFiles) {
lightClasses.add(new FakeLightClassForFileOfPackage(psiManager, lightClassForFacade, file)); lightClasses.add(FakeLightClassForFileOfPackage(psiManager, lightClassForFacade, file))
} }
} }
return lightClasses; return lightClasses
} }
@NotNull override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
@Override val result = ArrayList<PsiClass>()
public Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) { val facadeClassesInfos = findFacadeClassesInfos(facadeFqName, scope)
List<PsiClass> result = new ArrayList<PsiClass>(); for (info in facadeClassesInfos) {
List<KotlinLightFacadeClassInfo> facadeClassesInfos = findFacadeClassesInfos(facadeFqName, scope); val files = PackagePartClassUtils.getFilesWithCallables(info.files)
for (KotlinLightFacadeClassInfo info : facadeClassesInfos) { if (files.isEmpty()) continue
List<JetFile> files = PackagePartClassUtils.getFilesWithCallables(info.getFiles());
if (files.isEmpty()) continue;
IdeaModuleInfo moduleInfo = info.getModuleInfo(); val moduleInfo = info.moduleInfo
if (moduleInfo instanceof ModuleSourceInfo) { if (moduleInfo is ModuleSourceInfo) {
result.addAll(getLightClassesForStaticFacadeWithSources(facadeFqName, files, moduleInfo)); result.addAll(getLightClassesForStaticFacadeWithSources(facadeFqName, files, moduleInfo))
} }
else { else {
result.addAll(getLightClassesForDecompiledFacadeFiles(files)); result.addAll(getLightClassesForDecompiledFacadeFiles(files))
} }
} }
return result; return result
} }
private List<PsiClass> getLightClassesForStaticFacadeWithSources( private fun getLightClassesForStaticFacadeWithSources(
@NotNull FqName facadeFqName, facadeFqName: FqName,
@NotNull List<JetFile> facadeFiles, facadeFiles: List<JetFile>,
@NotNull IdeaModuleInfo moduleInfo moduleInfo: IdeaModuleInfo): List<PsiClass> {
) { val lightClassForFacade = KotlinLightClassForFacade.createForFacade(
KotlinLightClassForFacade lightClassForFacade = KotlinLightClassForFacade.Factory.createForFacade( psiManager, facadeFqName, moduleInfo.contentScope(), facadeFiles)
psiManager, facadeFqName, moduleInfo.contentScope(), facadeFiles); return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles)
return getLightClassesForFacadeWithFiles(lightClassForFacade, facadeFiles);
} }
@NotNull override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<JetFile> {
@Override return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourcesAndLibraries(scope, project), project)
public Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourcesAndLibraries(scope, project), project);
} }
@NotNull private fun findFacadeClassesInfos(facadeFqName: FqName, scope: GlobalSearchScope): List<KotlinLightFacadeClassInfo> {
private List<KotlinLightFacadeClassInfo> findFacadeClassesInfos(FqName facadeFqName, GlobalSearchScope scope) { val facadeFiles = findFilesForFacade(facadeFqName, scope)
Collection<JetFile> facadeFiles = findFilesForFacade(facadeFqName, scope); return groupFilesIntoFacadeLightClassesByModuleInfo(facadeFiles)
return groupFilesIntoFacadeLightClassesByModuleInfo(facadeFiles);
} }
@NotNull override fun resolveClassToDescriptor(classOrObject: JetClassOrObject): ClassDescriptor? {
private static List<KotlinLightFacadeClassInfo> groupFilesIntoFacadeLightClassesByModuleInfo(Collection<JetFile> facadeFiles) {
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = KotlinPackage.groupByTo(
facadeFiles,
new LinkedHashMap<IdeaModuleInfo, List<JetFile>>(),
new Function1<JetFile, IdeaModuleInfo>() {
@Override
public IdeaModuleInfo invoke(JetFile file) {
return ResolvePackage.getModuleInfo(file);
}
});
List<KotlinLightFacadeClassInfo> result = new ArrayList<KotlinLightFacadeClassInfo>();
for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) {
result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey()));
}
return result;
}
@Nullable
@Override
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) {
try { try {
return (ClassDescriptor) ResolvePackage.resolveToDescriptor(classOrObject); return classOrObject.resolveToDescriptor() as ClassDescriptor
} }
catch (NoDescriptorForDeclarationException e) { catch (e: NoDescriptorForDeclarationException) {
return null; return null
} }
} }
@NotNull private fun findPackageClassesInfos(
private static List<PsiClass> getLightClassesForDecompiledFacadeFiles(@NotNull List<JetFile> filesWithCallables) { fqName: FqName, wholeScope: GlobalSearchScope): List<KotlinLightFacadeClassInfo> {
List<PsiClass> lightClasses = new ArrayList<PsiClass>(); val allFiles = findFilesForPackage(fqName, wholeScope)
for (JetFile file : filesWithCallables) { return groupFilesIntoFacadeLightClassesByModuleInfo(allFiles)
if (file.isCompiled()) {
lightClasses.add(createLightClassForDecompiledKotlinFile(file));
}
}
return lightClasses;
} }
@NotNull private class KotlinLightFacadeClassInfo(public val files: Collection<JetFile>, public val moduleInfo: IdeaModuleInfo)
private List<KotlinLightFacadeClassInfo> findPackageClassesInfos(
@NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope
) {
Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope);
return groupFilesIntoFacadeLightClassesByModuleInfo(allFiles);
}
private static final class KotlinLightFacadeClassInfo { private class CachedJavaStub(public var modificationStamp: Long, public var javaFileStub: PsiJavaFileStubImpl)
private final Collection<JetFile> files;
private final IdeaModuleInfo moduleInfo;
public KotlinLightFacadeClassInfo(@NotNull Collection<JetFile> files, @NotNull IdeaModuleInfo moduleInfo) { companion object {
this.files = files;
this.moduleInfo = moduleInfo;
}
@NotNull private val LOG = Logger.getInstance(javaClass<IDELightClassGenerationSupport>())
public Collection<JetFile> getFiles() {
return files;
}
@NotNull private fun forceResolvePackageDeclarations(files: Collection<JetFile>, session: KotlinCodeAnalyzer) {
public IdeaModuleInfo getModuleInfo() { for (file in files) {
return moduleInfo; // SCRIPT: not supported
} if (file.isScript) continue
}
@Nullable val packageFqName = file.packageFqName
private static KotlinLightClassForDecompiledDeclaration createLightClassForDecompiledKotlinFile(@NotNull JetFile file) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) {
return null;
}
JetClassOrObject classOrObject = singleOrNull(filterIsInstance(file.getDeclarations(), JetClassOrObject.class)); // make sure we create a package descriptor
val packageDescriptor = session.moduleDescriptor.getPackage(packageFqName)
ClsClassImpl javaClsClass = createClsJavaClassFromVirtualFile(file, virtualFile, classOrObject); if (packageDescriptor.isEmpty()) {
if (javaClsClass == null) { LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.name + "\n" + file.text)
return null; session.forceResolveAll()
} continue
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) {
return decompiledClassOrObject.getNavigationElement().getContainingFile();
} }
return super.getNavigationElement();
for (declaration in file.declarations) {
if (declaration is JetFunction) {
val name = declaration.nameAsSafeName
val functions = packageDescriptor.memberScope.getFunctions(name, NoLookupLocation.FROM_IDE)
for (descriptor in functions) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
else if (declaration is JetProperty) {
val name = declaration.nameAsSafeName
val properties = packageDescriptor.memberScope.getProperties(name, NoLookupLocation.FROM_IDE)
for (descriptor in properties) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
else if (declaration is JetClassOrObject) {
// Do nothing: we are not interested in classes
}
else {
LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.name + "\n" + file.text)
}
}
}
}
private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: JetClassOrObject): PsiClass? {
if (decompiledClassOrObject is JetEnumEntry) {
return null
}
val containingJetFile = decompiledClassOrObject.getContainingJetFile()
if (!containingJetFile.isCompiled) {
return null
}
val rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingJetFile) ?: return null
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile)
}
private fun findCorrespondingLightClass(
decompiledClassOrObject: JetClassOrObject,
rootLightClassForDecompiledFile: PsiClass): PsiClass {
val relativeFqName = getClassRelativeName(decompiledClassOrObject)
val iterator = relativeFqName.pathSegments().iterator()
val base = iterator.next()
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 }
var current = rootLightClassForDecompiledFile
while (iterator.hasNext()) {
val name = iterator.next()
val innerClass = current.findInnerClassByName(name.asString(), false)
assert(innerClass != null) { "Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.fqName + "\n" + "File: " + decompiledClassOrObject.getContainingJetFile().virtualFile.name }
current = innerClass
}
return current
}
private fun getClassRelativeName(decompiledClassOrObject: JetClassOrObject): FqName {
val name = decompiledClassOrObject.nameAsName
assert(name != null)
val parent = PsiTreeUtil.getParentOfType(decompiledClassOrObject, javaClass<JetClassOrObject>(), true)
if (parent == null) {
assert(decompiledClassOrObject.isTopLevel())
return FqName.topLevel(name)
}
return getClassRelativeName(parent).child(name)
}
private fun groupFilesIntoFacadeLightClassesByModuleInfo(facadeFiles: Collection<JetFile>): List<KotlinLightFacadeClassInfo> {
val filesByInfo = KotlinPackage.groupByTo(
facadeFiles,
LinkedHashMap<IdeaModuleInfo, List<JetFile>>(),
object : Function1<JetFile, IdeaModuleInfo> {
override fun invoke(file: JetFile): IdeaModuleInfo {
return file.getModuleInfo()
}
})
val result = ArrayList<KotlinLightFacadeClassInfo>()
for (entry in filesByInfo.entrySet()) {
result.add(KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey()))
}
return result
}
private fun getLightClassesForDecompiledFacadeFiles(filesWithCallables: List<JetFile>): List<PsiClass> {
val lightClasses = ArrayList<PsiClass>()
for (file in filesWithCallables) {
if (file.isCompiled) {
lightClasses.add(createLightClassForDecompiledKotlinFile(file))
}
}
return lightClasses
}
private fun createLightClassForDecompiledKotlinFile(file: JetFile): KotlinLightClassForDecompiledDeclaration? {
val virtualFile = file.virtualFile ?: return null
val classOrObject = singleOrNull<JetClassOrObject>(filterIsInstance<JetClassOrObject>(file.declarations, javaClass<JetClassOrObject>()))
val javaClsClass = createClsJavaClassFromVirtualFile(file, virtualFile, classOrObject) ?: return null
return KotlinLightClassForDecompiledDeclaration(javaClsClass, classOrObject)
}
private fun createClsJavaClassFromVirtualFile(
decompiledKotlinFile: JetFile,
virtualFile: VirtualFile,
decompiledClassOrObject: JetClassOrObject?): ClsClassImpl? {
val javaFileStub = getOrCreateJavaFileStub(decompiledKotlinFile, virtualFile) ?: return null
val manager = PsiManager.getInstance(decompiledKotlinFile.project)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
override fun getNavigationElement(): PsiElement {
if (decompiledClassOrObject != null) {
return decompiledClassOrObject.navigationElement.containingFile
}
return super.getNavigationElement()
}
override fun getStub(): PsiClassHolderFileStub<PsiFile> {
return javaFileStub
}
override fun getMirror(): PsiElement {
return decompiledKotlinFile
}
}
fakeFile.isPhysical = false
javaFileStub.setPsi(fakeFile)
return single<PsiClass>(fakeFile.classes) as ClsClassImpl
}
private val cachedJavaStubKey = Key.create<CachedJavaStub>("CACHED_JAVA_STUB")
private fun getOrCreateJavaFileStub(
decompiledKotlinFile: JetFile,
virtualFile: VirtualFile): PsiJavaFileStubImpl? {
val cachedJavaStub = decompiledKotlinFile.getUserData(cachedJavaStubKey)
val fileModificationStamp = virtualFile.modificationStamp
if (cachedJavaStub != null && cachedJavaStub.modificationStamp == fileModificationStamp) {
return cachedJavaStub.javaFileStub
}
val stub = createStub(virtualFile) as PsiJavaFileStubImpl?
if (stub != null) {
decompiledKotlinFile.putUserData(cachedJavaStubKey, CachedJavaStub(fileModificationStamp, stub))
}
return stub
}
private fun createStub(file: VirtualFile): PsiJavaFileStub? {
if (file.fileType !== JavaClassFileType.INSTANCE) return null
try {
return ClsFileImpl.buildFileStub(file, file.contentsToByteArray())
}
catch (e: ClsFormatException) {
LOG.debug(e)
}
catch (e: IOException) {
LOG.debug(e)
} }
@NotNull LOG.error("Failed to build java cls class for " + file.canonicalPath!!)
@Override return null
public PsiClassHolderFileStub getStub() {
return javaFileStub;
}
@Override
public PsiElement getMirror() {
return decompiledKotlinFile;
}
};
fakeFile.setPhysical(false);
javaFileStub.setPsi(fakeFile);
return (ClsClassImpl) single(fakeFile.getClasses());
}
private final static Key<CachedJavaStub> cachedJavaStubKey = Key.create("CACHED_JAVA_STUB");
private static class CachedJavaStub {
public CachedJavaStub(long modificationStamp, @NotNull PsiJavaFileStubImpl javaFileStub) {
this.modificationStamp = modificationStamp;
this.javaFileStub = javaFileStub;
} }
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) {
return cachedJavaStub.javaFileStub;
}
PsiJavaFileStubImpl stub = (PsiJavaFileStubImpl) createStub(virtualFile);
if (stub != null) {
decompiledKotlinFile.putUserData(cachedJavaStubKey, new CachedJavaStub(fileModificationStamp, stub));
}
return stub;
}
@Nullable
private static PsiJavaFileStub createStub(@NotNull VirtualFile file) {
if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
try {
return ClsFileImpl.buildFileStub(file, file.contentsToByteArray());
}
catch (ClsFormatException e) {
LOG.debug(e);
}
catch (IOException e) {
LOG.debug(e);
}
LOG.error("Failed to build java cls class for " + file.getCanonicalPath());
return null;
} }
} }