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)
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))
}
@@ -91,4 +91,6 @@ import java.util.*
public @jvmStatic fun getFilesForPart(partFqName: FqName, files: Collection<JetFile>): List<JetFile> =
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 fun getPackageFqName(): FqName
public fun getFacadeSimpleName(): String?
public fun isScript(): Boolean
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)
throws IOException {
dataStream.writeName(stub.getPackageFqName().asString());
dataStream.writeName(stub.getFacadeSimpleName());
dataStream.writeBoolean(stub.isScript());
}
@@ -76,7 +77,8 @@ public class JetFileElementType extends IStubFileElementType<KotlinFileStub> {
public KotlinFileStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
StringRef packageFqNameAsString = dataStream.readName();
boolean isScript = dataStream.readBoolean();
return new KotlinFileStubImpl(null, packageFqNameAsString, isScript);
StringRef facadeSimpleName = dataStream.readName();
return new KotlinFileStubImpl(null, packageFqNameAsString, facadeSimpleName, isScript);
}
@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.elements.JetStubElementTypes
public class KotlinFileStubImpl(jetFile: JetFile?, private val packageName: StringRef, private val isScript: Boolean)// SCRIPT: PsiJetFileStubImpl knows about scripting
: PsiFileStubImpl<JetFile>(jetFile), KotlinFileStub, PsiClassHolderFileStub<JetFile> {
// SCRIPT: PsiJetFileStubImpl knows about scripting
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)
: this(jetFile, StringRef.fromString(packageName)!!, isScript) {
}
: this(jetFile, StringRef.fromString(packageName)!!, null, isScript)
override fun getPackageFqName(): FqName = FqName(StringRef.toString(packageName)!!)
override fun getFacadeSimpleName(): String? = StringRef.toString(facadeShortName)
override fun isScript(): Boolean = isScript
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()
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.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.compiled.ClsClassImpl;
import com.intellij.psi.impl.compiled.ClsFileImpl;
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.util.ProjectRootsUtil;
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.name.FqName;
import org.jetbrains.kotlin.name.Name;
@@ -64,6 +62,7 @@ import java.util.*;
import static kotlin.KotlinPackage.*;
import static org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourceAndClassFiles;
import static org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope.kotlinSourcesAndLibraries;
public class IDELightClassGenerationSupport extends LightClassGenerationSupport {
@@ -187,19 +186,6 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
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
@Override
public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
@@ -294,27 +280,43 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
IdeaModuleInfo moduleInfo = info.getModuleInfo();
if (moduleInfo instanceof ModuleSourceInfo) {
KotlinLightClassForFacade lightClass =
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));
}
}
result.addAll(getLightClassesForPackageFacadeWithSources(packageFqName, files, moduleInfo));
}
else {
FqName packageFacadeFqName = PackageClassUtils.getPackageClassFqName(packageFqName);
List<PsiClass> clsClasses = getLightClassesForDecompiledFacadeFiles(packageFacadeFqName, files);
result.addAll(clsClasses);
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) {
@@ -326,36 +328,48 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
IdeaModuleInfo moduleInfo = info.getModuleInfo();
if (moduleInfo instanceof ModuleSourceInfo) {
KotlinLightClassForFacade lightClass =
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));
}
}
result.addAll(getLightClassesForStaticFacadeWithSources(facadeFqName, files, moduleInfo));
}
else {
List<PsiClass> clsClasses = getLightClassesForDecompiledFacadeFiles(facadeFqName, files);
result.addAll(clsClasses);
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, kotlinSourceAndClassFiles(scope, project), project);
return StaticFacadeIndexUtil.findFilesForStaticFacade(facadeFqName, kotlinSourcesAndLibraries(scope, project), project);
}
@NotNull
private List<KotlinLightFacadeClassInfo> findFacadeClassesInfos(FqName facadeFqName, GlobalSearchScope 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>();
for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) {
result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey()));
@@ -374,8 +388,8 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
}
}
@Nullable
private static List<PsiClass> getLightClassesForDecompiledFacadeFiles(@NotNull FqName facadeFqName, @NotNull List<JetFile> filesWithCallables) {
@NotNull
private static List<PsiClass> getLightClassesForDecompiledFacadeFiles(@NotNull List<JetFile> filesWithCallables) {
List<PsiClass> lightClasses = new ArrayList<PsiClass>();
for (JetFile file : filesWithCallables) {
if (file.isCompiled()) {
@@ -390,12 +404,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
@NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope
) {
Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope);
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = groupByModuleInfo(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;
return groupFilesIntoFacadeLightClassesByModuleInfo(allFiles);
}
private static final class KotlinLightFacadeClassInfo {
@@ -445,7 +454,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return null;
}
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
@Override
public PsiElement getNavigationElement() {
@@ -68,7 +68,7 @@ public open class KotlinClsStubBuilder : ClsStubBuilder() {
header.isCompatiblePackageFacadeKind() -> {
val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData)
val context = components.createContext(packageData.getNameResolver(), packageFqName)
createPackageFacadeFileStub(packageData.getPackageProto(), packageFqName, context)
createPackageFacadeStub(packageData.getPackageProto(), packageFqName, context)
}
header.isCompatibleClassKind() -> {
if (header.classKind != JvmAnnotationNames.KotlinClass.Kind.CLASS) return null
@@ -79,7 +79,7 @@ public open class KotlinClsStubBuilder : ClsStubBuilder() {
header.isCompatibleFileFacadeKind() -> {
val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData)
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")
}
@@ -63,7 +63,7 @@ public open class KotlinJavaScriptStubBuilder : ClsStubBuilder() {
if (isPackageHeader) {
val packageData = content.toPackageData(nameResolver)
val context = components.createContext(packageData.getNameResolver(), packageFqName)
return createPackageFacadeFileStub(packageData.getPackageProto(), packageFqName, context)
return createPackageFacadeStub(packageData.getPackageProto(), packageFqName, context)
}
else {
val classData = content.toClassData(nameResolver)
@@ -42,12 +42,13 @@ fun createTopLevelClassStub(classId: ClassId, classProto: ProtoBuf.Class, contex
return fileStub
}
fun createPackageFacadeFileStub(
fun createPackageFacadeStub(
packageProto: ProtoBuf.Package,
packageFqName: FqName,
c: ClsStubBuilderContext
): KotlinFileStubImpl {
val fileStub = createFileStub(packageFqName)
val fileStub = KotlinFileStubImpl.forPackageStub(packageFqName, packageFqName.isRoot)
setupFileStub(fileStub, packageFqName)
val container = ProtoContainer(null, packageFqName)
for (callableProto in packageProto.getMemberList()) {
createCallableStub(fileStub, callableProto, c, container)
@@ -55,14 +56,33 @@ fun createPackageFacadeFileStub(
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 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)
createStubForPackageName(packageDirectiveStub, packageFqName)
KotlinPlaceHolderStubImpl<JetImportList>(fileStub, JetStubElementTypes.IMPORT_LIST)
return fileStub
}
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))
companion object {
private val KEY = KotlinIndexUtil.createIndexKey(javaClass<JetStaticFacadeClassIndex>())
private val KEY = KotlinIndexUtil.createIndexKey(JetStaticFacadeClassIndex::class.java)
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.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile
import kotlin.platform.platformStatic
public object StaticFacadeIndexUtil {
platformStatic public fun findFilesForStaticFacade(
@jvmStatic public fun findFilesForStaticFacade(
facadeFqName: FqName,
searchScope: GlobalSearchScope,
project: Project
) : Collection<JetFile> =
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 com.intellij.psi.stubs.StubElement;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.JetClassOrObject;
import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.psi.stubs.*;
@@ -36,11 +37,18 @@ public class StubIndexServiceImpl implements StubIndexService {
sink.occurrence(JetExactPackagesIndex.getInstance().getKey(), packageFqName.asString());
JetFile psi = stub.getPsi();
if (psi != null) {
FqName staticFacadeFqName = PackagePartClassUtils.getPackagePartFqName(psi);
String facadeSimpleName = stub.getFacadeSimpleName();
if (facadeSimpleName != null) {
FqName staticFacadeFqName = packageFqName.child(Name.identifier(facadeSimpleName));
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
@@ -28,8 +28,6 @@ import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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.resolve.jvm.JvmClassName;
@@ -53,38 +51,25 @@ public class JetExceptionFilter implements Filter {
StackTraceElement element = parseStackTraceLine(line);
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();
// fullyQualifiedName is of format "package.Class$Inner"
String fullyQualifiedName = element.getClassName();
// All classes except package classes, package parts and top level closures are handled correctly in the default ExceptionFilter
if (!isPackageClassOrPackagePartPrefixedName(fullyQualifiedName)) return null;
int lineNumber = element.getLineNumber() - 1;
String internalName = fullyQualifiedName.replace('.', '/');
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;
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) return null;
return new OpenFileHyperlinkInfo(project, virtualFile, element.getLineNumber() - 1);
}
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);
return new OpenFileHyperlinkInfo(project, virtualFile, lineNumber);
}
// 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.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.LibraryUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
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.FunctionDescriptor;
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.DescriptorToSourceUtilsIde;
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.PackagePartClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.CompositeBindingContext;
@@ -56,6 +59,8 @@ public class DebuggerUtils {
private DebuggerUtils() {
}
private static final Set<String> KOTLIN_EXTENSIONS = Sets.newHashSet("kt", "kts");
@Nullable
public static JetFile findSourceFileForClass(
@NotNull Project project,
@@ -64,6 +69,9 @@ public class DebuggerUtils {
@NotNull final String fileName,
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> filesWithExactName = Collections2.filter(filesInPackage, new Predicate<JetFile>() {
@Override
@@ -78,6 +86,19 @@ public class DebuggerUtils {
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)) {
for (JetFile file : filesWithExactName) {
boolean hasTopLevelMembers = KotlinPackage.any(file.getDeclarations(), new Function1<JetDeclaration, Boolean>() {
@@ -108,10 +129,11 @@ public class DebuggerUtils {
@Override
public Boolean invoke(JetFile file) {
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);
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" })
}
public fun testKotlinClass() {
val kotlinClassFqName = "A"
doTest("a.kt", 3, { kotlinClassFqName })
}
public fun testLibrarySources() {
val mockLibrary = MockLibraryUtil.compileLibraryToJar(getTestDataPath() + getTestRoot() + "mockLibrary", "mockLibrary", true)