From 7e356354e0b99ba27a5b8b9aa728815a32f61e9b Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Wed, 7 Oct 2015 16:05:35 +0300 Subject: [PATCH] Debugger: fix breakpoints in files with JvmName annotation Drop unnessecary logic about part classes with hashcodes #KT-9480 Fixed --- .../idea/debugger/JetPositionManager.kt | 24 +--- ...PositionManagerLibraryClassFileSearcher.kt | 129 ------------------ .../idea/filters/JetExceptionFilter.java | 2 +- .../kotlin/idea/util/DebuggerUtils.java | 61 +-------- .../positionManager/JvmNameAnnotation.kt | 6 + .../AbstractJetPositionManagerTest.java | 52 +++++-- .../JetPositionManagerTestGenerated.java | 6 + 7 files changed, 58 insertions(+), 222 deletions(-) delete mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/PositionManagerLibraryClassFileSearcher.kt create mode 100644 idea/testData/debugger/positionManager/JvmNameAnnotation.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt index 60cc98924e1..41f0785c7ee 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/JetPositionManager.kt @@ -41,6 +41,8 @@ import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.JetTypeMapper import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider +import org.jetbrains.kotlin.fileClasses.getFileClassInternalName import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils @@ -50,10 +52,8 @@ import org.jetbrains.kotlin.idea.util.DebuggerUtils import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.application.runReadAction 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.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.jvm.JvmClassName @@ -80,7 +80,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult val javaClassName = JvmClassName.byInternalName(defaultInternalName(location)) val project = myDebugProcess.project - val defaultPsiFile = DebuggerUtils.findSourceFileForClass(project, GlobalSearchScope.allScope(project), javaClassName, javaSourceFileName, 0) + val defaultPsiFile = DebuggerUtils.findSourceFileForClass(project, GlobalSearchScope.allScope(project), javaClassName, javaSourceFileName) if (defaultPsiFile != null) { return SourcePosition.createFromLine(defaultPsiFile, 0) } @@ -178,7 +178,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult val project = myDebugProcess.project - return DebuggerUtils.findSourceFileForClass(project, GlobalSearchScope.allScope(project), className, sourceName, location.lineNumber() - 1) + return DebuggerUtils.findSourceFileForClass(project, GlobalSearchScope.allScope(project), className, sourceName) } private fun defaultInternalName(location: Location): String { @@ -375,19 +375,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult } } - if (isInLibrary) { - val elementAtForLibraryFile = - if (element is JetDeclaration) element - else PsiTreeUtil.getParentOfType(element, JetDeclaration::class.java) - - assert(elementAtForLibraryFile != null) { - "Couldn't find element at breakpoint for library file " + file.name + - (if (notPositionedElement == null) "" else ", notPositionedElement = " + notPositionedElement.getElementTextWithContext()) - } - return PositionedElement(findPackagePartInternalNameForLibraryFile(elementAtForLibraryFile!!), elementAtForLibraryFile) - } - - return PositionedElement(PackagePartClassUtils.getPackagePartInternalName(file), element) + return PositionedElement(NoResolveFileClassesProvider.getFileClassInternalName(file), element) } private val TYPES_TO_CALCULATE_CLASSNAME: Array> = @@ -439,7 +427,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult return InlineUtil.isInlinedArgument(functionLiteral, context, false) } - private fun createKeyForTypeMapper(file: JetFile) = PackagePartClassUtils.getPackagePartInternalName(file) + private fun createKeyForTypeMapper(file: JetFile) = NoResolveFileClassesProvider.getFileClassInternalName(file) } private fun findInlinedCalls(element: PsiElement?, jetFile: PsiFile?): List { diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/PositionManagerLibraryClassFileSearcher.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/PositionManagerLibraryClassFileSearcher.kt deleted file mode 100644 index 37b37d4cff8..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/PositionManagerLibraryClassFileSearcher.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.debugger - -import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.module.impl.scopes.JdkScope -import com.intellij.openapi.module.impl.scopes.LibraryScope -import com.intellij.openapi.roots.JdkOrderEntry -import com.intellij.openapi.roots.LibraryOrderEntry -import com.intellij.openapi.roots.libraries.LibraryUtil -import com.intellij.psi.search.FilenameIndex -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils -import org.jetbrains.kotlin.psi.JetDeclaration -import org.jetbrains.kotlin.psi.JetElement -import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.resolve.jvm.JvmClassName -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor - -private val LOG = Logger.getInstance("org.jetbrains.kotlin.idea.debugger") - -/** - * This method finds class name for top-level declaration in source file attached to library. - * The problem is that package-part class file has hash in className and it depends on machine where library was built, - * so we couldn't predict it. - * 1. find all .class files with package-part prefix, if there is one - return it - * 2. find all descriptors with same signature, if there is one - return it - * 3. else -> return null, because it means that there is more than one function with same signature in project - */ -fun findPackagePartInternalNameForLibraryFile(topLevelDeclaration: JetDeclaration): String? { - val packagePartFile = findPackagePartFileNamesForElement(topLevelDeclaration).singleOrNull() - if (packagePartFile != null) return packagePartFile - - - val descriptor = topLevelDeclaration.resolveToDescriptor() - if (descriptor !is CallableDescriptor) return null - - val packageFqName = topLevelDeclaration.getContainingJetFile().getPackageFqName() - val packageDescriptor = descriptor.module.getPackage(packageFqName) - val descFromSourceText = render(descriptor) - - val descriptors: Collection = when (descriptor) { - is FunctionDescriptor -> packageDescriptor.memberScope.getFunctions(descriptor.name, NoLookupLocation.FROM_IDE) - is PropertyDescriptor -> packageDescriptor.memberScope.getProperties(descriptor.name, NoLookupLocation.FROM_IDE) - else -> { - reportError(topLevelDeclaration, descriptor) - listOf() - } - } - - val deserializedDescriptor = descriptors - .filterIsInstance() - .filter { render(it) == descFromSourceText } - .singleOrNull() - - if (deserializedDescriptor == null) { - reportError(topLevelDeclaration, descriptor) - return null - } - - val implClassName = JvmFileClassUtil.getImplClassName(deserializedDescriptor) - if (implClassName != null) { - return JvmClassName.byFqNameWithoutInnerClasses(packageFqName.child(implClassName)).getInternalName() - } - - return null -} - -private fun findPackagePartFileNamesForElement(elementAt: JetElement): List { - val project = elementAt.getProject() - val file = elementAt.getContainingJetFile() - - val packagePartName = PackagePartClassUtils.getPackagePartFqName(file).shortName().asString() - val packagePartNameWoHash = packagePartName.substring(0, packagePartName.lastIndexOf("$") + 1) - - val libraryEntry = LibraryUtil.findLibraryEntry(file.getVirtualFile(), project) - val scope = if (libraryEntry is LibraryOrderEntry){ - LibraryScope(project, libraryEntry.getLibrary() ?: throw AssertionError("Cannot find library for file ${file.getVirtualFile()?.getPath()}")) - } - else { - JdkScope(project, libraryEntry as JdkOrderEntry) - } - - val packagePartFiles = FilenameIndex.getAllFilenames(project).asSequence().filter { - it.startsWith(packagePartNameWoHash) && it.endsWith(".class") && - !it.substringAfter(packagePartNameWoHash).contains("\$") - }.flatMap { - FilenameIndex.getVirtualFilesByName(project, it, scope).asSequence() - }.map { - val packageFqName = file.getPackageFqName() - if (packageFqName.isRoot()) { - it.getNameWithoutExtension() - } else { - "${packageFqName.asString()}.${it.getNameWithoutExtension()}" - } - } - return packagePartFiles.toList() -} - -private fun render(desc: DeclarationDescriptor) = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(desc) - -private fun reportError(element: JetElement, descriptor: CallableDescriptor?) { - LOG.error("Couldn't calculate class name for element in library scope:\n" + - element.getElementTextWithContext() + - if (descriptor != null) "\ndescriptor = ${render(descriptor)}" else "" - ) -} diff --git a/idea/src/org/jetbrains/kotlin/idea/filters/JetExceptionFilter.java b/idea/src/org/jetbrains/kotlin/idea/filters/JetExceptionFilter.java index 20653ba115b..7304a040153 100644 --- a/idea/src/org/jetbrains/kotlin/idea/filters/JetExceptionFilter.java +++ b/idea/src/org/jetbrains/kotlin/idea/filters/JetExceptionFilter.java @@ -63,7 +63,7 @@ public class JetExceptionFilter implements Filter { String internalName = fullyQualifiedName.replace('.', '/'); JvmClassName jvmClassName = JvmClassName.byInternalName(internalName); - JetFile file = DebuggerUtils.findSourceFileForClass(project, searchScope, jvmClassName, fileName, lineNumber); + JetFile file = DebuggerUtils.findSourceFileForClass(project, searchScope, jvmClassName, fileName); if (file == null) return null; VirtualFile virtualFile = file.getVirtualFile(); diff --git a/idea/src/org/jetbrains/kotlin/idea/util/DebuggerUtils.java b/idea/src/org/jetbrains/kotlin/idea/util/DebuggerUtils.java index 2cc5abd6510..a60f83b1deb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/DebuggerUtils.java +++ b/idea/src/org/jetbrains/kotlin/idea/util/DebuggerUtils.java @@ -22,11 +22,9 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.intellij.openapi.project.DumbService; 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; import kotlin.KotlinPackage; import kotlin.Pair; import kotlin.jvm.functions.Function1; @@ -35,13 +33,10 @@ 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.codeInsight.CodeInsightUtils; import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde; -import org.jetbrains.kotlin.idea.debugger.DebuggerPackage; 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; @@ -66,9 +61,8 @@ public class DebuggerUtils { public static JetFile findSourceFileForClass( @NotNull Project project, @NotNull GlobalSearchScope searchScope, - @NotNull final JvmClassName className, - @NotNull final String fileName, - final int lineNumber + @NotNull JvmClassName className, + @NotNull final String fileName ) { String extension = FileUtilRt.getExtension(fileName); if (!KOTLIN_EXTENSIONS.contains(extension)) return null; @@ -113,66 +107,15 @@ public class DebuggerUtils { } } - if (isPackagePartClassName(className)) { - JetFile file = getFileForPackagePartPrefixedName(filesWithExactName, className.getInternalName()); - if (file != null) { - return file; - } - - boolean isInLibrary = KotlinPackage.all(filesWithExactName, new Function1() { - @Override - public Boolean invoke(JetFile file) { - return LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null; - } - }); - - if (isInLibrary) { - return KotlinPackage.singleOrNull(KotlinPackage.filter(filesWithExactName, new Function1() { - @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; - JetDeclaration elementAt = PsiTreeUtil.getParentOfType(file.findElementAt(startLineOffset), JetDeclaration.class); - return elementAt != null && - className.getInternalName().equals(DebuggerPackage.findPackagePartInternalNameForLibraryFile(elementAt)); - } - })); - } - - return null; - } - return filesWithExactName.iterator().next(); } - private static boolean isPackagePartClassName(JvmClassName className) { - String packageName = PackageClassUtils.getPackageClassInternalName(className.getPackageFqName()); - - String internalName = className.getInternalName(); - return !internalName.equals(packageName) && internalName.startsWith(packageName); - } - private static boolean isPackageClassName(JvmClassName className) { String packageName = PackageClassUtils.getPackageClassInternalName(className.getPackageFqName()); return packageName.equals(className.getInternalName()); } - @Nullable - private static JetFile getFileForPackagePartPrefixedName( - @NotNull Collection allPackageFiles, - @NotNull String classInternalName - ) { - for (JetFile file : allPackageFiles) { - String packagePartInternalName = PackagePartClassUtils.getPackagePartInternalName(file); - if (classInternalName.startsWith(packagePartInternalName)) { - return file; - } - } - return null; - } - @NotNull public static Pair> analyzeInlinedFunctions( @NotNull ResolutionFacade resolutionFacadeForFile, diff --git a/idea/testData/debugger/positionManager/JvmNameAnnotation.kt b/idea/testData/debugger/positionManager/JvmNameAnnotation.kt new file mode 100644 index 00000000000..324eeb484ef --- /dev/null +++ b/idea/testData/debugger/positionManager/JvmNameAnnotation.kt @@ -0,0 +1,6 @@ +@file:JvmName("ABC") +package a + +fun foo() { + "" // a/ABC +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractJetPositionManagerTest.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractJetPositionManagerTest.java index 6280991c308..4848abea588 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractJetPositionManagerTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractJetPositionManagerTest.java @@ -28,23 +28,27 @@ import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.testFramework.PsiTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; import com.sun.jdi.Location; import com.sun.jdi.ReferenceType; -import kotlin.KotlinPackage; +import kotlin.SequencesKt; +import kotlin.StringsKt; +import kotlin.Unit; +import kotlin.io.FilesKt; +import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.backend.common.output.OutputFile; import org.jetbrains.kotlin.backend.common.output.OutputFileCollection; import org.jetbrains.kotlin.codegen.GenerationUtils; import org.jetbrains.kotlin.codegen.state.GenerationState; -import org.jetbrains.kotlin.idea.test.KotlinMultiFileTestCase; -import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider; +import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase; +import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor; +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.utils.UtilsPackage; +import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -52,7 +56,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -public abstract class AbstractJetPositionManagerTest extends KotlinMultiFileTestCase { +public abstract class AbstractJetPositionManagerTest extends JetLightCodeInsightFixtureTestCase { // Breakpoint is given as a line comment on a specific line, containing the regexp to match the name of the class where that line // can be found. This pattern matches against these line comments and saves the class name in the first group private static final Pattern BREAKPOINT_PATTERN = Pattern.compile("^.*//\\s*(.+)\\s*$"); @@ -60,15 +64,21 @@ public abstract class AbstractJetPositionManagerTest extends KotlinMultiFileTest @NotNull @Override protected String getTestDataPath() { - return PluginTestCaseBase.getTestDataPathBase(); + return PluginTestCaseBase.getTestDataPathBase() + "/debugger/positionManager/"; + } + + @Override + public void setUp() { + super.setUp(); + myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase()); } private DebugProcessImpl debugProcess; @NotNull @Override - protected String getTestRoot() { - return "/debugger/positionManager/"; + protected LightProjectDescriptor getProjectDescriptor() { + return JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE; } @NotNull @@ -89,18 +99,30 @@ public abstract class AbstractJetPositionManagerTest extends KotlinMultiFileTest protected void doTest(@NotNull String fileName) throws Exception { if (fileName.endsWith(".kt")) { - String path = KotlinPackage.substringAfter(fileName, PluginTestCaseBase.TEST_DATA_PROJECT_RELATIVE.substring(1), fileName); - configureByFile(path); + String path = getPath(fileName); + myFixture.configureByFile(path); } else { - VirtualFile rootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, fileName, myFilesToDelete, false); - prepareProject(rootDir); - PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + String path = getPath(fileName); + SequencesKt.forEach(FilesKt.walkTopDown(new File(path)), new Function1() { + @Override + public Unit invoke(File file) { + String fileName = file.getName(); + String path = getPath(fileName); + myFixture.configureByFile(path); + return null; + } + }); } performTest(); } + @NotNull + private static String getPath(@NotNull String fileName) { + return StringsKt.substringAfter(fileName, PluginTestCaseBase.TEST_DATA_PROJECT_RELATIVE.substring(1), fileName); + } + private void performTest() { Project project = getProject(); List files = new ArrayList(PluginJetFilesProvider.allFilesInProject(project)); diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/JetPositionManagerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/JetPositionManagerTestGenerated.java index 8c23d6fab13..81f3c9f4f13 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/JetPositionManagerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/JetPositionManagerTestGenerated.java @@ -91,6 +91,12 @@ public class JetPositionManagerTestGenerated extends AbstractJetPositionManagerT doTest(fileName); } + @TestMetadata("JvmNameAnnotation.kt") + public void testJvmNameAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/positionManager/JvmNameAnnotation.kt"); + doTest(fileName); + } + @TestMetadata("localFunction.kt") public void testLocalFunction() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/positionManager/localFunction.kt");