Fix breakpoints in inline functions after dexing (KT-12896)
#KT-12896 In Progress
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user