Fix debugger tests.

- Index binaries by facade class fqName
- Fix DebuggerUtils: should understand new facades
- Refactor IDELightClassGenerationSupport
- Add facadeSimpleName to KotlinFileStub
This commit is contained in:
Dmitry Petrov
2015-09-01 17:31:43 +03:00
committed by Michael Bogdanov
parent f9901f6075
commit 5ca8f7409a
15 changed files with 185 additions and 98 deletions
@@ -59,7 +59,7 @@ import java.util.*
getPackagePartFqName(facadeClassFqName.parent(), file.name) getPackagePartFqName(facadeClassFqName.parent(), file.name)
public @jvmStatic fun getPackagePartFqName(packageFqName: FqName, fileName: String): FqName { public @jvmStatic fun getPackagePartFqName(packageFqName: FqName, fileName: String): FqName {
val partClassName = getPartClassName(FileUtil.getNameWithoutExtension(fileName)) val partClassName = getFilePartShortName(fileName)
return packageFqName.child(Name.identifier(partClassName)) return packageFqName.child(Name.identifier(partClassName))
} }
@@ -91,4 +91,6 @@ import java.util.*
public @jvmStatic fun getFilesForPart(partFqName: FqName, files: Collection<JetFile>): List<JetFile> = public @jvmStatic fun getFilesForPart(partFqName: FqName, files: Collection<JetFile>): List<JetFile> =
getFilesWithCallables(files).filter { getPackagePartFqName(it) == partFqName } getFilesWithCallables(files).filter { getPackagePartFqName(it) == partFqName }
public @jvmStatic fun getFilePartShortName(fileName: String): String =
getPartClassName(FileUtil.getNameWithoutExtension(fileName))
} }
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.psi.*
public interface KotlinFileStub : PsiFileStub<JetFile> { public interface KotlinFileStub : PsiFileStub<JetFile> {
public fun getPackageFqName(): FqName public fun getPackageFqName(): FqName
public fun getFacadeSimpleName(): String?
public fun isScript(): Boolean public fun isScript(): Boolean
public fun findImportsByAlias(alias: String): List<KotlinImportDirectiveStub> public fun findImportsByAlias(alias: String): List<KotlinImportDirectiveStub>
} }
@@ -68,6 +68,7 @@ public class JetFileElementType extends IStubFileElementType<KotlinFileStub> {
public void serialize(@NotNull KotlinFileStub stub, @NotNull StubOutputStream dataStream) public void serialize(@NotNull KotlinFileStub stub, @NotNull StubOutputStream dataStream)
throws IOException { throws IOException {
dataStream.writeName(stub.getPackageFqName().asString()); dataStream.writeName(stub.getPackageFqName().asString());
dataStream.writeName(stub.getFacadeSimpleName());
dataStream.writeBoolean(stub.isScript()); dataStream.writeBoolean(stub.isScript());
} }
@@ -76,7 +77,8 @@ public class JetFileElementType extends IStubFileElementType<KotlinFileStub> {
public KotlinFileStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { public KotlinFileStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
StringRef packageFqNameAsString = dataStream.readName(); StringRef packageFqNameAsString = dataStream.readName();
boolean isScript = dataStream.readBoolean(); boolean isScript = dataStream.readBoolean();
return new KotlinFileStubImpl(null, packageFqNameAsString, isScript); StringRef facadeSimpleName = dataStream.readName();
return new KotlinFileStubImpl(null, packageFqNameAsString, facadeSimpleName, isScript);
} }
@Override @Override
@@ -28,14 +28,19 @@ import org.jetbrains.kotlin.psi.stubs.KotlinFileStub
import org.jetbrains.kotlin.psi.stubs.KotlinImportDirectiveStub import org.jetbrains.kotlin.psi.stubs.KotlinImportDirectiveStub
import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes
public class KotlinFileStubImpl(jetFile: JetFile?, private val packageName: StringRef, private val isScript: Boolean)// SCRIPT: PsiJetFileStubImpl knows about scripting // SCRIPT: PsiJetFileStubImpl knows about scripting
: PsiFileStubImpl<JetFile>(jetFile), KotlinFileStub, PsiClassHolderFileStub<JetFile> { public class KotlinFileStubImpl(
jetFile: JetFile?,
private val packageName: StringRef,
private val facadeShortName: StringRef?,
private val isScript: Boolean
) : PsiFileStubImpl<JetFile>(jetFile), KotlinFileStub, PsiClassHolderFileStub<JetFile> {
public constructor(jetFile: JetFile?, packageName: String, isScript: Boolean) public constructor(jetFile: JetFile?, packageName: String, isScript: Boolean)
: this(jetFile, StringRef.fromString(packageName)!!, isScript) { : this(jetFile, StringRef.fromString(packageName)!!, null, isScript)
}
override fun getPackageFqName(): FqName = FqName(StringRef.toString(packageName)!!) override fun getPackageFqName(): FqName = FqName(StringRef.toString(packageName)!!)
override fun getFacadeSimpleName(): String? = StringRef.toString(facadeShortName)
override fun isScript(): Boolean = isScript override fun isScript(): Boolean = isScript
override fun getType(): IStubFileElementType<KotlinFileStub> = JetStubElementTypes.FILE override fun getType(): IStubFileElementType<KotlinFileStub> = JetStubElementTypes.FILE
@@ -49,4 +54,18 @@ public class KotlinFileStubImpl(jetFile: JetFile?, private val packageName: Stri
val importList = childrenStubs.firstOrNull { it.stubType == JetStubElementTypes.IMPORT_LIST } ?: return emptyList() val importList = childrenStubs.firstOrNull { it.stubType == JetStubElementTypes.IMPORT_LIST } ?: return emptyList()
return importList.childrenStubs.filterIsInstance<KotlinImportDirectiveStub>().filter { it.getAliasName() == alias } return importList.childrenStubs.filterIsInstance<KotlinImportDirectiveStub>().filter { it.getAliasName() == alias }
} }
companion object {
public fun forPackageStub(packageFqName: FqName, isScript: Boolean): KotlinFileStubImpl =
KotlinFileStubImpl(null,
StringRef.fromString(packageFqName.asString())!!,
null,
isScript)
public fun forFileFacadeStub(facadeFqName: FqName, isScript: Boolean): KotlinFileStubImpl =
KotlinFileStubImpl(null,
StringRef.fromString(facadeFqName.parent().asString())!!,
StringRef.fromString(facadeFqName.shortName().asString())!!,
isScript)
}
} }
@@ -26,7 +26,6 @@ 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.PsiManagerImpl;
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;
@@ -49,7 +48,6 @@ import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil;
import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil; import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil;
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil; import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
@@ -64,6 +62,7 @@ import java.util.*;
import static kotlin.KotlinPackage.*; import static kotlin.KotlinPackage.*;
import static org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourceAndClassFiles; import static org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourceAndClassFiles;
import static org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourcesAndLibraries;
public class IDELightClassGenerationSupport extends LightClassGenerationSupport { public class IDELightClassGenerationSupport extends LightClassGenerationSupport {
@@ -187,19 +186,6 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSourceAndClassFiles(searchScope, project), project); return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSourceAndClassFiles(searchScope, project), project);
} }
@NotNull
private static Map<IdeaModuleInfo, List<JetFile>> groupByModuleInfo(@NotNull Collection<JetFile> allFiles) {
return KotlinPackage.groupByTo(
allFiles,
new LinkedHashMap<IdeaModuleInfo, List<JetFile>>(),
new Function1<JetFile, IdeaModuleInfo>() {
@Override
public IdeaModuleInfo invoke(JetFile file) {
return ResolvePackage.getModuleInfo(file);
}
});
}
@NotNull @NotNull
@Override @Override
public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage( public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
@@ -294,27 +280,43 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
IdeaModuleInfo moduleInfo = info.getModuleInfo(); IdeaModuleInfo moduleInfo = info.getModuleInfo();
if (moduleInfo instanceof ModuleSourceInfo) { if (moduleInfo instanceof ModuleSourceInfo) {
KotlinLightClassForFacade lightClass = result.addAll(getLightClassesForPackageFacadeWithSources(packageFqName, files, moduleInfo));
KotlinLightClassForFacade.Factory.createForPackageFacade(psiManager, packageFqName, moduleInfo.contentScope(), files);
if (lightClass == null) continue;
result.add(lightClass);
if (files.size() > 1) {
for (JetFile file : files) {
result.add(new FakeLightClassForFileOfPackage(psiManager, lightClass, file));
}
}
} }
else { else {
FqName packageFacadeFqName = PackageClassUtils.getPackageClassFqName(packageFqName); result.addAll(getLightClassesForDecompiledFacadeFiles(files));
List<PsiClass> clsClasses = getLightClassesForDecompiledFacadeFiles(packageFacadeFqName, files);
result.addAll(clsClasses);
} }
} }
return result; 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 @NotNull
@Override @Override
public Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) { public Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
@@ -326,36 +328,48 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
IdeaModuleInfo moduleInfo = info.getModuleInfo(); IdeaModuleInfo moduleInfo = info.getModuleInfo();
if (moduleInfo instanceof ModuleSourceInfo) { if (moduleInfo instanceof ModuleSourceInfo) {
KotlinLightClassForFacade lightClass = result.addAll(getLightClassesForStaticFacadeWithSources(facadeFqName, files, moduleInfo));
KotlinLightClassForFacade.Factory.createForFacade(psiManager, facadeFqName, moduleInfo.contentScope(), files);
if (lightClass == null) continue;
result.add(lightClass);
if (files.size() > 1) {
for (JetFile file : files) {
result.add(new FakeLightClassForFileOfPackage(psiManager, lightClass, file));
}
}
} }
else { else {
List<PsiClass> clsClasses = getLightClassesForDecompiledFacadeFiles(facadeFqName, files); result.addAll(getLightClassesForDecompiledFacadeFiles(files));
result.addAll(clsClasses);
} }
} }
return result; 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 @NotNull
@Override @Override
public Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) { public Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourceAndClassFiles(scope, project), project); return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourcesAndLibraries(scope, project), project);
} }
@NotNull @NotNull
private List<KotlinLightFacadeClassInfo> findFacadeClassesInfos(FqName facadeFqName, GlobalSearchScope scope) { private List<KotlinLightFacadeClassInfo> findFacadeClassesInfos(FqName facadeFqName, GlobalSearchScope scope) {
Collection<JetFile> facadeFiles = findFilesForFacade(facadeFqName, scope); Collection<JetFile> facadeFiles = findFilesForFacade(facadeFqName, scope);
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = groupByModuleInfo(facadeFiles); return groupFilesIntoFacadeLightClassesByModuleInfo(facadeFiles);
}
@NotNull
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>(); List<KotlinLightFacadeClassInfo> result = new ArrayList<KotlinLightFacadeClassInfo>();
for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) { for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) {
result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey())); result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey()));
@@ -374,8 +388,8 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
} }
} }
@Nullable @NotNull
private static List<PsiClass> getLightClassesForDecompiledFacadeFiles(@NotNull FqName facadeFqName, @NotNull List<JetFile> filesWithCallables) { private static List<PsiClass> getLightClassesForDecompiledFacadeFiles(@NotNull List<JetFile> filesWithCallables) {
List<PsiClass> lightClasses = new ArrayList<PsiClass>(); List<PsiClass> lightClasses = new ArrayList<PsiClass>();
for (JetFile file : filesWithCallables) { for (JetFile file : filesWithCallables) {
if (file.isCompiled()) { if (file.isCompiled()) {
@@ -390,12 +404,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
@NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope @NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope
) { ) {
Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope); Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope);
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = groupByModuleInfo(allFiles); return groupFilesIntoFacadeLightClassesByModuleInfo(allFiles);
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;
} }
private static final class KotlinLightFacadeClassInfo { private static final class KotlinLightFacadeClassInfo {
@@ -445,7 +454,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return null; return null;
} }
PsiManager manager = PsiManager.getInstance(decompiledKotlinFile.getProject()); PsiManager manager = PsiManager.getInstance(decompiledKotlinFile.getProject());
ClsFileImpl fakeFile = new ClsFileImpl((PsiManagerImpl) manager, new ClassFileViewProvider(manager, virtualFile)) { ClsFileImpl fakeFile = new ClsFileImpl(new ClassFileViewProvider(manager, virtualFile)) {
@NotNull @NotNull
@Override @Override
public PsiElement getNavigationElement() { public PsiElement getNavigationElement() {
@@ -68,7 +68,7 @@ public open class KotlinClsStubBuilder : ClsStubBuilder() {
header.isCompatiblePackageFacadeKind() -> { header.isCompatiblePackageFacadeKind() -> {
val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData) val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData)
val context = components.createContext(packageData.getNameResolver(), packageFqName) val context = components.createContext(packageData.getNameResolver(), packageFqName)
createPackageFacadeFileStub(packageData.getPackageProto(), packageFqName, context) createPackageFacadeStub(packageData.getPackageProto(), packageFqName, context)
} }
header.isCompatibleClassKind() -> { header.isCompatibleClassKind() -> {
if (header.classKind != JvmAnnotationNames.KotlinClass.Kind.CLASS) return null if (header.classKind != JvmAnnotationNames.KotlinClass.Kind.CLASS) return null
@@ -79,7 +79,7 @@ public open class KotlinClsStubBuilder : ClsStubBuilder() {
header.isCompatibleFileFacadeKind() -> { header.isCompatibleFileFacadeKind() -> {
val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData) val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData)
val context = components.createContext(packageData.getNameResolver(), packageFqName) val context = components.createContext(packageData.getNameResolver(), packageFqName)
createPackageFacadeFileStub(packageData.getPackageProto(), packageFqName, context) createFileFacadeStub(packageData.getPackageProto(), classId.asSingleFqName(), context)
} }
else -> throw IllegalStateException("Should have processed " + file.getPath() + " with header $header") else -> throw IllegalStateException("Should have processed " + file.getPath() + " with header $header")
} }
@@ -63,7 +63,7 @@ public open class KotlinJavaScriptStubBuilder : ClsStubBuilder() {
if (isPackageHeader) { if (isPackageHeader) {
val packageData = content.toPackageData(nameResolver) val packageData = content.toPackageData(nameResolver)
val context = components.createContext(packageData.getNameResolver(), packageFqName) val context = components.createContext(packageData.getNameResolver(), packageFqName)
return createPackageFacadeFileStub(packageData.getPackageProto(), packageFqName, context) return createPackageFacadeStub(packageData.getPackageProto(), packageFqName, context)
} }
else { else {
val classData = content.toClassData(nameResolver) val classData = content.toClassData(nameResolver)
@@ -42,12 +42,13 @@ fun createTopLevelClassStub(classId: ClassId, classProto: ProtoBuf.Class, contex
return fileStub return fileStub
} }
fun createPackageFacadeFileStub( fun createPackageFacadeStub(
packageProto: ProtoBuf.Package, packageProto: ProtoBuf.Package,
packageFqName: FqName, packageFqName: FqName,
c: ClsStubBuilderContext c: ClsStubBuilderContext
): KotlinFileStubImpl { ): KotlinFileStubImpl {
val fileStub = createFileStub(packageFqName) val fileStub = KotlinFileStubImpl.forPackageStub(packageFqName, packageFqName.isRoot)
setupFileStub(fileStub, packageFqName)
val container = ProtoContainer(null, packageFqName) val container = ProtoContainer(null, packageFqName)
for (callableProto in packageProto.getMemberList()) { for (callableProto in packageProto.getMemberList()) {
createCallableStub(fileStub, callableProto, c, container) createCallableStub(fileStub, callableProto, c, container)
@@ -55,14 +56,33 @@ fun createPackageFacadeFileStub(
return fileStub return fileStub
} }
fun createFileFacadeStub(
packageProto: ProtoBuf.Package,
facadeFqName: FqName,
c: ClsStubBuilderContext
): KotlinFileStubImpl {
val packageFqName = facadeFqName.parent()
val fileStub = KotlinFileStubImpl.forFileFacadeStub(facadeFqName, packageFqName.isRoot)
setupFileStub(fileStub, packageFqName)
val container = ProtoContainer(null, facadeFqName.parent())
for (callableProto in packageProto.getMemberList()) {
createCallableStub(fileStub, callableProto, c, container)
}
return fileStub
}
fun createIncompatibleAbiVersionFileStub() = createFileStub(FqName.ROOT) fun createIncompatibleAbiVersionFileStub() = createFileStub(FqName.ROOT)
fun createFileStub(packageFqName: FqName): KotlinFileStubImpl { fun createFileStub(packageFqName: FqName): KotlinFileStubImpl {
val fileStub = KotlinFileStubImpl(null, packageFqName.asString(), packageFqName.isRoot()) val fileStub = KotlinFileStubImpl.forPackageStub(packageFqName, packageFqName.isRoot)
setupFileStub(fileStub, packageFqName)
return fileStub
}
private fun setupFileStub(fileStub: KotlinFileStubImpl, packageFqName: FqName) {
val packageDirectiveStub = KotlinPlaceHolderStubImpl<JetPackageDirective>(fileStub, JetStubElementTypes.PACKAGE_DIRECTIVE) val packageDirectiveStub = KotlinPlaceHolderStubImpl<JetPackageDirective>(fileStub, JetStubElementTypes.PACKAGE_DIRECTIVE)
createStubForPackageName(packageDirectiveStub, packageFqName) createStubForPackageName(packageDirectiveStub, packageFqName)
KotlinPlaceHolderStubImpl<JetImportList>(fileStub, JetStubElementTypes.IMPORT_LIST) KotlinPlaceHolderStubImpl<JetImportList>(fileStub, JetStubElementTypes.IMPORT_LIST)
return fileStub
} }
fun createStubForPackageName(packageDirectiveStub: KotlinPlaceHolderStubImpl<JetPackageDirective>, packageFqName: FqName) { fun createStubForPackageName(packageDirectiveStub: KotlinPlaceHolderStubImpl<JetPackageDirective>, packageFqName: FqName) {
@@ -29,10 +29,10 @@ public class JetStaticFacadeClassIndex private constructor() : StringStubIndexEx
super.get(key, project, JetSourceFilterScope.kotlinSourcesAndLibraries(scope, project)) super.get(key, project, JetSourceFilterScope.kotlinSourcesAndLibraries(scope, project))
companion object { companion object {
private val KEY = KotlinIndexUtil.createIndexKey(javaClass<JetStaticFacadeClassIndex>()) private val KEY = KotlinIndexUtil.createIndexKey(JetStaticFacadeClassIndex::class.java)
public val INSTANCE: JetStaticFacadeClassIndex = JetStaticFacadeClassIndex() public val INSTANCE: JetStaticFacadeClassIndex = JetStaticFacadeClassIndex()
public fun getInstance(): JetStaticFacadeClassIndex = INSTANCE public @jvmStatic fun getInstance(): JetStaticFacadeClassIndex = INSTANCE
} }
} }
@@ -18,17 +18,26 @@ package org.jetbrains.kotlin.idea.stubindex
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetFile
import kotlin.platform.platformStatic import kotlin.platform.platformStatic
public object StaticFacadeIndexUtil { public object StaticFacadeIndexUtil {
platformStatic public fun findFilesForStaticFacade( @jvmStatic public fun findFilesForStaticFacade(
facadeFqName: FqName, facadeFqName: FqName,
searchScope: GlobalSearchScope, searchScope: GlobalSearchScope,
project: Project project: Project
) : Collection<JetFile> = ) : Collection<JetFile> =
JetStaticFacadeClassIndex.INSTANCE.get(facadeFqName.asString(), project, searchScope) JetStaticFacadeClassIndex.INSTANCE.get(facadeFqName.asString(), project, searchScope)
// TODO change as we introduce multi-file facades (this will require a separate index)
@jvmStatic public fun findFilesForFilePart(
partFqName: FqName,
searchScope: GlobalSearchScope,
project: Project
) : Collection<JetFile> =
PackagePartClassUtils.getFilesWithCallables(
JetStaticFacadeClassIndex.INSTANCE.get(partFqName.asString(), project, searchScope))
} }
@@ -20,6 +20,7 @@ import com.intellij.psi.stubs.IndexSink;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubElement;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.JetClassOrObject; import org.jetbrains.kotlin.psi.JetClassOrObject;
import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.psi.stubs.*; import org.jetbrains.kotlin.psi.stubs.*;
@@ -36,11 +37,18 @@ public class StubIndexServiceImpl implements StubIndexService {
sink.occurrence(JetExactPackagesIndex.getInstance().getKey(), packageFqName.asString()); sink.occurrence(JetExactPackagesIndex.getInstance().getKey(), packageFqName.asString());
JetFile psi = stub.getPsi(); String facadeSimpleName = stub.getFacadeSimpleName();
if (psi != null) { if (facadeSimpleName != null) {
FqName staticFacadeFqName = PackagePartClassUtils.getPackagePartFqName(psi); FqName staticFacadeFqName = packageFqName.child(Name.identifier(facadeSimpleName));
sink.occurrence(JetStaticFacadeClassIndex.INSTANCE.getKey(), staticFacadeFqName.asString()); sink.occurrence(JetStaticFacadeClassIndex.INSTANCE.getKey(), staticFacadeFqName.asString());
} }
else {
JetFile psi = stub.getPsi();
if (psi != null) {
FqName staticFacadeFqName = PackagePartClassUtils.getPackagePartFqName(psi);
sink.occurrence(JetStaticFacadeClassIndex.INSTANCE.getKey(), staticFacadeFqName.asString());
}
}
} }
@Override @Override
@@ -28,8 +28,6 @@ import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.util.DebuggerUtils; import org.jetbrains.kotlin.idea.util.DebuggerUtils;
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
@@ -53,38 +51,25 @@ public class JetExceptionFilter implements Filter {
StackTraceElement element = parseStackTraceLine(line); StackTraceElement element = parseStackTraceLine(line);
if (element == null) return null; if (element == null) return null;
// All true classes should be handled correctly in the default ExceptionFilter. Special cases:
// - static facades;
// - package facades / package parts (generated by pre-M13 compiled);
// - local classes (and closures) in top-level function and property declarations.
String fileName = element.getFileName(); String fileName = element.getFileName();
// fullyQualifiedName is of format "package.Class$Inner" // fullyQualifiedName is of format "package.Class$Inner"
String fullyQualifiedName = element.getClassName(); String fullyQualifiedName = element.getClassName();
int lineNumber = element.getLineNumber() - 1;
// All classes except package classes, package parts and top level closures are handled correctly in the default ExceptionFilter
if (!isPackageClassOrPackagePartPrefixedName(fullyQualifiedName)) return null;
String internalName = fullyQualifiedName.replace('.', '/'); String internalName = fullyQualifiedName.replace('.', '/');
JvmClassName jvmClassName = JvmClassName.byInternalName(internalName); JvmClassName jvmClassName = JvmClassName.byInternalName(internalName);
JetFile file = DebuggerUtils.findSourceFileForClass(project, searchScope, jvmClassName, fileName, element.getLineNumber() - 1); JetFile file = DebuggerUtils.findSourceFileForClass(project, searchScope, jvmClassName, fileName, lineNumber);
if (file == null) return null; if (file == null) return null;
VirtualFile virtualFile = file.getVirtualFile(); VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) return null; if (virtualFile == null) return null;
return new OpenFileHyperlinkInfo(project, virtualFile, element.getLineNumber() - 1); return new OpenFileHyperlinkInfo(project, virtualFile, lineNumber);
}
private static boolean isPackageClassOrPackagePartPrefixedName(String fqName) {
if (fqName.equals(PackageClassUtils.getPackageClassName(FqName.ROOT))) {
return true;
}
int lastDot = fqName.lastIndexOf('.');
String classNameWithInners = fqName.substring(lastDot + 1);
int firstDollar = classNameWithInners.indexOf('$');
String className = firstDollar >= 0 ? classNameWithInners.substring(0, firstDollar) : classNameWithInners;
String packageClassName = PackageClassUtils.getPackageClassName(new FqName(fqName).parent());
return packageClassName.equals(className);
} }
// Matches strings like "\tat test.TestPackage$foo$f$1.invoke(a.kt:3)\n" // Matches strings like "\tat test.TestPackage$foo$f$1.invoke(a.kt:3)\n"
@@ -19,8 +19,10 @@ package org.jetbrains.kotlin.idea.util;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
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.io.FileUtilRt;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
@@ -32,13 +34,14 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade;
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils; import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils;
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde; import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde;
import org.jetbrains.kotlin.idea.debugger.DebuggerPackage; import org.jetbrains.kotlin.idea.debugger.DebuggerPackage;
import org.jetbrains.kotlin.idea.resolve.ResolvePackage; import org.jetbrains.kotlin.idea.resolve.ResolutionFacade;
import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil;
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.CompositeBindingContext; import org.jetbrains.kotlin.resolve.CompositeBindingContext;
@@ -56,6 +59,8 @@ public class DebuggerUtils {
private DebuggerUtils() { private DebuggerUtils() {
} }
private static final Set<String> KOTLIN_EXTENSIONS = Sets.newHashSet("kt", "kts");
@Nullable @Nullable
public static JetFile findSourceFileForClass( public static JetFile findSourceFileForClass(
@NotNull Project project, @NotNull Project project,
@@ -64,6 +69,9 @@ public class DebuggerUtils {
@NotNull final String fileName, @NotNull final String fileName,
final int lineNumber final int lineNumber
) { ) {
String extension = FileUtilRt.getExtension(fileName);
if (!KOTLIN_EXTENSIONS.contains(extension)) return null;
Collection<JetFile> filesInPackage = findFilesWithExactPackage(className.getPackageFqName(), searchScope, project); Collection<JetFile> filesInPackage = findFilesWithExactPackage(className.getPackageFqName(), searchScope, project);
Collection<JetFile> filesWithExactName = Collections2.filter(filesInPackage, new Predicate<JetFile>() { Collection<JetFile> filesWithExactName = Collections2.filter(filesInPackage, new Predicate<JetFile>() {
@Override @Override
@@ -78,6 +86,19 @@ public class DebuggerUtils {
return filesWithExactName.iterator().next(); return filesWithExactName.iterator().next();
} }
// Static facade or inner class of such facade?
FqName partFqName = className.getFqNameForClassNameWithoutDollars();
Collection<JetFile> filesForPart = StaticFacadeIndexUtil.findFilesForFilePart(partFqName, searchScope, project);
if (!filesForPart.isEmpty()) {
for (JetFile file : filesForPart) {
if (file.getName().equals(fileName)) {
return file;
}
}
// Do not fall back to decompiled files (which have different name).
return null;
}
if (isPackageClassName(className)) { if (isPackageClassName(className)) {
for (JetFile file : filesWithExactName) { for (JetFile file : filesWithExactName) {
boolean hasTopLevelMembers = KotlinPackage.any(file.getDeclarations(), new Function1<JetDeclaration, Boolean>() { boolean hasTopLevelMembers = KotlinPackage.any(file.getDeclarations(), new Function1<JetDeclaration, Boolean>() {
@@ -108,10 +129,11 @@ public class DebuggerUtils {
@Override @Override
public Boolean invoke(JetFile file) { public Boolean invoke(JetFile file) {
Integer startLineOffset = CodeInsightUtils.getStartLineOffset(file, lineNumber); Integer startLineOffset = CodeInsightUtils.getStartLineOffset(file, lineNumber);
assert startLineOffset != null : "Cannot find start line offset for file " + file.getName() + ", line " + lineNumber; assert startLineOffset != null : "Cannot find start line offset for file " + file.getName() + ", line " +
lineNumber;
JetDeclaration elementAt = PsiTreeUtil.getParentOfType(file.findElementAt(startLineOffset), JetDeclaration.class); JetDeclaration elementAt = PsiTreeUtil.getParentOfType(file.findElementAt(startLineOffset), JetDeclaration.class);
return elementAt != null && return elementAt != null &&
className.getInternalName().equals(DebuggerPackage.findPackagePartInternalNameForLibraryFile(elementAt)); className.getInternalName().equals(DebuggerPackage.findPackagePartInternalNameForLibraryFile(elementAt));
} }
})); }));
} }
@@ -0,0 +1,5 @@
class A {
fun foo() {
println("Hello!")
}
}
@@ -129,6 +129,11 @@ public class JetExceptionFilterTest : MultiFileTestCase() {
doTest("2/foo2.kt", 4, { file -> "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, file) + "\$foo\$f\$1" }) doTest("2/foo2.kt", 4, { file -> "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, file) + "\$foo\$f\$1" })
} }
public fun testKotlinClass() {
val kotlinClassFqName = "A"
doTest("a.kt", 3, { kotlinClassFqName })
}
public fun testLibrarySources() { public fun testLibrarySources() {
val mockLibrary = MockLibraryUtil.compileLibraryToJar(getTestDataPath() + getTestRoot() + "mockLibrary", "mockLibrary", true) val mockLibrary = MockLibraryUtil.compileLibraryToJar(getTestDataPath() + getTestRoot() + "mockLibrary", "mockLibrary", true)