Fix breakpoints in inline functions after dexing (KT-12896)

#KT-12896 In Progress
This commit is contained in:
Nikolay Krasko
2016-07-21 15:37:17 +03:00
parent 64979ae190
commit 5df7358dab
24 changed files with 289 additions and 27 deletions
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil.findFilesWithExactPackage
import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CompositeBindingContext
@@ -46,11 +47,13 @@ object DebuggerUtils {
scope: GlobalSearchScope,
className: JvmClassName,
fileName: String): KtFile? {
return findSourceFileForClass(
project,
listOf(scope, KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)),
className,
fileName)
return runReadAction {
findSourceFileForClass(
project,
listOf(scope, KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)),
className,
fileName)
}
}
fun findSourceFileForClass(
@@ -256,12 +256,24 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
throw NoDataException.INSTANCE
}
try {
if (myDebugProcess.isDexDebug()) {
val inlineLocations = noStrataLocationsOfLineForInlineFunctions(type, position, myDebugProcess.searchScope)
if (!inlineLocations.isEmpty()) {
return inlineLocations
}
}
val line = position.line + 1
val locations = if (myDebugProcess.virtualMachineProxy.versionHigher("1.4"))
type.locationsOfLine(KOTLIN_STRATA_NAME, null, line).filter { it.sourceName(KOTLIN_STRATA_NAME) == position.file.name }
else
type.locationsOfLine(line)
if (locations == null || locations.isEmpty()) throw NoDataException.INSTANCE
if (locations == null || locations.isEmpty()) {
throw NoDataException.INSTANCE
}
return locations
}
catch (e: AbsentInformationException) {
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2016 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.debugger.SourcePosition
import com.intellij.psi.search.GlobalSearchScope
import com.sun.jdi.Location
import com.sun.jdi.ReferenceType
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.psiUtil.parents
fun noStrataLocationsOfLineForInlineFunctions(type: ReferenceType, position: SourcePosition, sourceSearchScope: GlobalSearchScope): List<Location> {
val line = position.line
val file = position.file
val project = position.file.project
val lineStartOffset = file.getLineStartOffset(line) ?: return listOf()
val element = file.findElementAt(lineStartOffset) ?: return listOf()
val isInInline = runReadAction { element.parents.any { it is KtFunction && it.hasModifier(KtTokens.INLINE_KEYWORD) } }
if (!isInInline) return listOf()
val lines = inlinedLinesNumbers(line + 1, position.file.name, FqName(type.name()), type.sourceName(), project, sourceSearchScope)
val inlineLocations = lines.flatMap { type.locationsOfLine(it) }
return inlineLocations
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.debugger
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.compiler.CompilerPaths
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
@@ -53,6 +54,27 @@ fun inlineLineAndFileByPosition(lineNumber: Int, fqName: FqName, fileName: Strin
return mapStacktraceLineToSource(smapData, lineNumber, project, SourceLineKind.EXECUTED_LINE, searchScope)
}
internal fun inlinedLinesNumbers(
inlineLineNumber: Int, inlineFileName: String,
destinationTypeFqName: FqName, destinationFileName: String,
project: Project, sourceSearchScope: GlobalSearchScope): List<Int> {
val internalName = destinationTypeFqName.asString().replace('.', '/')
val jvmClassName = JvmClassName.byInternalName(internalName)
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, sourceSearchScope, jvmClassName, destinationFileName) ?:
return listOf()
val virtualFile = file.virtualFile ?: return listOf()
val bytes = readClassFile(project, jvmClassName, virtualFile) ?: return listOf()
val smapData = readDebugInfo(bytes) ?: return listOf()
val smap = smapData.kotlinStrata ?: return listOf()
val mappingToInlinedFile = smap.fileMappings.firstOrNull() { it.name == inlineFileName } ?: return listOf()
return mappingToInlinedFile.lineMappings.map { it.mapSourceToDest(inlineLineNumber) }
}
fun isInlineFunctionLineNumber(file: VirtualFile, lineNumber: Int, project: Project): Boolean {
val linesInFile = file.toPsiFile(project)?.getLineCount() ?: return false
return lineNumber > linesInFile
@@ -79,9 +101,9 @@ fun readClassFile(project: Project,
val outputDir = CompilerPaths.getModuleOutputDirectory(module, /*forTests = */ false) ?: return null
val className = fqNameWithInners.asString().replace('.', '$')
val classByByDirectory = findClassFileByPath(jvmName.packageFqName.asString(), className, outputDir) ?: return null
val classByDirectory = findClassFileByPath(jvmName.packageFqName.asString(), className, outputDir) ?: return null
return classByByDirectory.readBytes()
return classByDirectory.readBytes()
}
else -> return null
@@ -94,6 +116,13 @@ private fun findClassFileByPath(packageName: String, className: String, outputDi
val parentDirectory = File(outDirFile, packageName.replace(".", File.separator))
if (!parentDirectory.exists()) return null
if (ApplicationManager.getApplication().isUnitTestMode) {
val beforeDexFileClassFile = File(parentDirectory, className + ".class.before_dex")
if (beforeDexFileClassFile.exists()) {
return beforeDexFileClassFile
}
}
val classFile = File(parentDirectory, className + ".class")
if (classFile.exists()) {
return classFile