diff --git a/.idea/dictionaries/Nikolay_Krasko.xml b/.idea/dictionaries/Nikolay_Krasko.xml
index 2995eb1ba67..abace9e7952 100644
--- a/.idea/dictionaries/Nikolay_Krasko.xml
+++ b/.idea/dictionaries/Nikolay_Krasko.xml
@@ -16,6 +16,7 @@
preloading
preprocess
redeclarations
+ smap
subclassed
subgraph
substep
diff --git a/idea/src/org/jetbrains/kotlin/idea/filters/KotlinExceptionFilter.kt b/idea/src/org/jetbrains/kotlin/idea/filters/KotlinExceptionFilter.kt
index d9f8cd23ac8..172c7832867 100644
--- a/idea/src/org/jetbrains/kotlin/idea/filters/KotlinExceptionFilter.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/filters/KotlinExceptionFilter.kt
@@ -20,34 +20,29 @@ import com.intellij.execution.filters.ExceptionFilter
import com.intellij.execution.filters.Filter
import com.intellij.execution.filters.HyperlinkInfo
import com.intellij.execution.filters.OpenFileHyperlinkInfo
-import com.intellij.openapi.compiler.CompilerPaths
import com.intellij.openapi.project.Project
-import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.GlobalSearchScope
-import org.jetbrains.kotlin.codegen.inline.FileMapping
-import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
-import org.jetbrains.kotlin.codegen.inline.SMAP
-import org.jetbrains.kotlin.codegen.inline.SMAPParser
-import org.jetbrains.kotlin.idea.refactoring.getLineCount
-import org.jetbrains.kotlin.idea.refactoring.toPsiFile
import org.jetbrains.kotlin.idea.util.DebuggerUtils
-import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
-import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinder
-import org.jetbrains.kotlin.name.ClassId
-import org.jetbrains.kotlin.name.Name
-import org.jetbrains.kotlin.name.tail
-import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
-import org.jetbrains.kotlin.utils.addToStdlib.check
-import org.jetbrains.org.objectweb.asm.ClassReader
-import org.jetbrains.org.objectweb.asm.ClassVisitor
-import java.io.File
import java.util.regex.Pattern
class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter {
private val exceptionFilter = ExceptionFilter(searchScope)
+ override fun applyFilter(line: String, entireLength: Int): Filter.Result? {
+ val result = exceptionFilter.applyFilter(line, entireLength)
+ return if (result == null) null else patchResult(result, line)
+ }
+
+ private fun patchResult(result: Filter.Result, line: String): Filter.Result {
+ val newHyperlinkInfo = createHyperlinkInfo(line) ?: return result
+
+ return Filter.Result(result.resultItems.map {
+ Filter.ResultItem(it.getHighlightStartOffset(), it.getHighlightEndOffset(), newHyperlinkInfo, it.getHighlightAttributes())
+ })
+ }
+
private fun createHyperlinkInfo(line: String): HyperlinkInfo? {
val project = searchScope.project ?: return null
@@ -69,7 +64,7 @@ class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter
val virtualFile = file.virtualFile ?: return null
- val hyperlinkInfoForInline = getHyperlinkInfoIfInline(jvmClassName, virtualFile, lineNumber + 1, project)
+ val hyperlinkInfoForInline = createHyperlinks(jvmClassName, virtualFile, lineNumber + 1, project)
if (hyperlinkInfoForInline != null) {
return hyperlinkInfoForInline
}
@@ -77,47 +72,30 @@ class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter
return OpenFileHyperlinkInfo(project, virtualFile, lineNumber)
}
- private fun getHyperlinkInfoIfInline(jvmName: JvmClassName, file: VirtualFile, lineNumber: Int, project: Project): InlineFunctionHyperLinkInfo? {
- val bytes = readClassFile(jvmName, file, project, { isInlineFunctionLineNumber(it, lineNumber, project) }) ?: return null
- return readDebugInfoForInlineFun(bytes, lineNumber, project)
- }
-
- private fun readDebugInfoForInlineFun(bytes: ByteArray, line: Int, project: Project): InlineFunctionHyperLinkInfo? {
+ private fun createHyperlinks(jvmName: JvmClassName, file: VirtualFile, line: Int, project: Project): InlineFunctionHyperLinkInfo? {
+ val bytes = readClassFile(project, jvmName, file,
+ sourceFileFilter = { sourceFile -> isInlineFunctionLineNumber(sourceFile, line, project) }) ?: return null
val smapData = readDebugInfo(bytes) ?: return null
- val inlineInfo = arrayListOf()
+ val inlineInfos = arrayListOf()
- val (inlineFunctionBodyFile, inlineFunctionBodyLine) = parseStrata(smapData.kotlin1, line, project, false, searchScope) ?: return null
- inlineInfo.add(InlineFunctionHyperLinkInfo.InlineInfo.InlineFunctionBodyInfo(
+ val (inlineFunctionBodyFile, inlineFunctionBodyLine) =
+ mapStacktraceLineToSource(smapData, line, project, SourceLineKind.EXECUTED_LINE, searchScope) ?: return null
+
+ inlineInfos.add(InlineFunctionHyperLinkInfo.InlineInfo.InlineFunctionBodyInfo(
inlineFunctionBodyFile.virtualFile,
inlineFunctionBodyLine))
- val kotlin2 = parseStrata(smapData.kotlin2, line, project, true, searchScope)
- if (kotlin2 != null) {
- inlineInfo.add(InlineFunctionHyperLinkInfo.InlineInfo.CallSiteInfo(
- kotlin2.first.virtualFile,
- kotlin2.second))
+ val inlineFunCallInfo = mapStacktraceLineToSource(smapData, line, project, SourceLineKind.CALL_LINE, searchScope)
+ if (inlineFunCallInfo != null) {
+ val (callSiteFile, callSiteLine) = inlineFunCallInfo
+ inlineInfos.add(InlineFunctionHyperLinkInfo.InlineInfo.CallSiteInfo(callSiteFile.virtualFile, callSiteLine))
}
- return InlineFunctionHyperLinkInfo(project, inlineInfo)
- }
-
- private fun patchResult(result: Filter.Result, line: String): Filter.Result {
- val newHyperlinkInfo = createHyperlinkInfo(line) ?: return result
-
- return Filter.Result(result.resultItems.map {
- Filter.ResultItem(it.getHighlightStartOffset(), it.getHighlightEndOffset(), newHyperlinkInfo, it.getHighlightAttributes())
- })
-
- }
-
- override fun applyFilter(line: String, entireLength: Int): Filter.Result? {
- val result = exceptionFilter.applyFilter(line, entireLength)
- return if (result == null) null else patchResult(result, line)
+ return InlineFunctionHyperLinkInfo(project, inlineInfos)
}
companion object {
-
// Matches strings like "\tat test.TestPackage$foo$f$1.invoke(a.kt:3)\n"
// or "\tBreakpoint reached at test.TestPackage$foo$f$1.invoke(a.kt:3)\n"
private val STACK_TRACE_ELEMENT_PATTERN = Pattern.compile("^[\\w|\\s]*at\\s+(.+)\\.(.+)\\((.+):(\\d+)\\)\\s*$")
diff --git a/idea/src/org/jetbrains/kotlin/idea/filters/InlineSmapPositionUtil.kt b/idea/src/org/jetbrains/kotlin/idea/filters/smapUtil.kt
similarity index 68%
rename from idea/src/org/jetbrains/kotlin/idea/filters/InlineSmapPositionUtil.kt
rename to idea/src/org/jetbrains/kotlin/idea/filters/smapUtil.kt
index 0a94b8446f7..34fa2cb7a9c 100644
--- a/idea/src/org/jetbrains/kotlin/idea/filters/InlineSmapPositionUtil.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/filters/smapUtil.kt
@@ -45,11 +45,15 @@ fun isInlineFunctionLineNumber(file: VirtualFile, lineNumber: Int, project: Proj
return lineNumber > linesInFile
}
-fun readClassFile(jvmName: JvmClassName, file: VirtualFile, project: Project, sourceCondition: (VirtualFile) -> Boolean = { true }): ByteArray? {
+fun readClassFile(project: Project,
+ jvmName: JvmClassName,
+ file: VirtualFile,
+ sourceFileFilter: (VirtualFile) -> Boolean = { true },
+ libFileFilter: (VirtualFile) -> Boolean = { true }): ByteArray? {
val fqNameWithInners = jvmName.fqNameForClassNameWithoutDollars.tail(jvmName.packageFqName)
when {
- ProjectRootsUtil.isLibrarySourceFile(project, file) -> {
+ ProjectRootsUtil.isLibrarySourceFile(project, file) && libFileFilter(file) -> {
val classId = ClassId(jvmName.packageFqName, Name.identifier(fqNameWithInners.asString()))
val fileFinder = JvmVirtualFileFinder.SERVICE.getInstance(project)
@@ -57,7 +61,7 @@ fun readClassFile(jvmName: JvmClassName, file: VirtualFile, project: Project, so
return classFile.contentsToByteArray()
}
- ProjectRootsUtil.isProjectSourceFile(project, file) && sourceCondition(file) -> {
+ ProjectRootsUtil.isProjectSourceFile(project, file) && sourceFileFilter(file) -> {
val module = ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(file)
val outputDir = CompilerPaths.getModuleOutputDirectory(module, /*forTests = */ false) ?: return null
@@ -72,7 +76,7 @@ fun readClassFile(jvmName: JvmClassName, file: VirtualFile, project: Project, so
}
private fun findClassFileByPath(packageName: String, className: String, outputDir: VirtualFile): File? {
- val outDirFile = File(outputDir.path).check { it.exists() } ?: return null
+ val outDirFile = File(outputDir.path).check(File::exists) ?: return null
val parentDirectory = File(outDirFile, packageName.replace(".", File.separator))
if (!parentDirectory.exists()) return null
@@ -85,20 +89,36 @@ private fun findClassFileByPath(packageName: String, className: String, outputDi
return null
}
-fun parseStrata(strata: String?, line: Int, project: Project, isKotlin2: Boolean, searchScope: GlobalSearchScope): Pair? {
- if (strata == null) return null
+enum class SourceLineKind {
+ CALL_LINE,
+ EXECUTED_LINE
+}
- val smap = SMAPParser.parse(strata)
+fun mapStacktraceLineToSource(smapData: SmapData,
+ line: Int,
+ project: Project,
+ lineKind: SourceLineKind,
+ searchScope: GlobalSearchScope): Pair? {
+ val smap = when (lineKind) {
+ SourceLineKind.CALL_LINE -> smapData.kotlinDebugStrata
+ SourceLineKind.EXECUTED_LINE -> smapData.kotlinStrata
+ } ?: return null
val mappingInfo = smap.fileMappings.firstOrNull {
it.getIntervalIfContains(line) != null
} ?: return null
- val newJvmName = JvmClassName.byInternalName(mappingInfo.path)
- val newSourceFile = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, searchScope, newJvmName, mappingInfo.name) ?: return null
+ val jvmName = JvmClassName.byInternalName(mappingInfo.path)
+ val sourceFile = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(
+ project, searchScope, jvmName, mappingInfo.name) ?: return null
val interval = mappingInfo.getIntervalIfContains(line)!!
- return newSourceFile to (if (isKotlin2) interval.source else interval.mapDestToSource(line)) - 1
+ val sourceLine = when (lineKind) {
+ SourceLineKind.CALL_LINE -> interval.source - 1
+ SourceLineKind.EXECUTED_LINE -> interval.mapDestToSource(line) - 1
+ }
+
+ return sourceFile to sourceLine
}
fun readDebugInfo(bytes: ByteArray): SmapData? {
@@ -109,24 +129,27 @@ fun readDebugInfo(bytes: ByteArray): SmapData? {
debugInfo = debug
}
}, ClassReader.SKIP_FRAMES and ClassReader.SKIP_CODE)
- return debugInfo?.let { SmapData(it) }
+ return debugInfo?.let(::SmapData)
}
class SmapData(debugInfo: String) {
- var kotlin1: String? = null
- private set
- var kotlin2: String? = null
- private set
+ var kotlinStrata: SMAP?
+ var kotlinDebugStrata: SMAP?
init {
- val intervals = debugInfo.split(SMAP.END).filter { it.isNotBlank() }
+ val intervals = debugInfo.split(SMAP.END).filter(String::isNotBlank)
when(intervals.count()) {
1 -> {
- kotlin1 = intervals[0] + SMAP.END
+ kotlinStrata = SMAPParser.parse(intervals[0] + SMAP.END)
+ kotlinDebugStrata = null
+ }
+ 2 -> {
+ kotlinStrata = SMAPParser.parse(intervals[0] + SMAP.END)
+ kotlinDebugStrata = SMAPParser.parse(intervals[1] + SMAP.END)
}
else -> {
- kotlin1 = intervals[0] + SMAP.END
- kotlin2 = intervals[1] + SMAP.END
+ kotlinStrata = null
+ kotlinDebugStrata = null
}
}
}