@@ -38,10 +38,6 @@ import org.jetbrains.kotlin.utils.getOrPutNullable
38
38
import org.jetbrains.org.objectweb.asm.*
39
39
import java.util.*
40
40
41
- // TODO: Don't read same bytecode file again and again
42
- // TODO: Build line mapping for the whole file
43
- // TODO: Quick caching for the same location
44
-
45
41
fun noStrataLineNumber (location : Location , isDexDebug : Boolean , project : Project , preferInlined : Boolean = false): Int {
46
42
if (isDexDebug) {
47
43
if (! preferInlined) {
@@ -68,33 +64,40 @@ fun getLastLineNumberForLocation(location: Location, project: Project, searchSco
68
64
val fileName = location.sourceName()
69
65
70
66
val method = location.method() ? : return null
67
+ val name = method.name() ? : return null
68
+ val signature = method.signature() ? : return null
71
69
72
- val bytes = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ? : return null
70
+ val debugInfo = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ? : return null
73
71
74
- fun readLineNumberTableMapping (bytes : ByteArray ): Map <String , Set <Int >> {
75
- val labelsToAllStrings = HashMap <String , MutableSet <Int >>()
72
+ val lineMapping = debugInfo.lineTableMapping[BytecodeMethodKey (name, signature)] ? : return null
73
+ return lineMapping.values.firstOrNull { it.contains(lineNumber) }?.last()
74
+ }
76
75
77
- ClassReader (bytes).accept(object : ClassVisitor (InlineCodegenUtil .API ) {
78
- override fun visitMethod (access : Int , name : String? , desc : String? , signature : String? , exceptions : Array <out String >? ): MethodVisitor ? {
79
- if (! (name == method.name() && desc == method.signature())) {
80
- return null
81
- }
76
+ fun readLineNumberTableMapping (bytes : ByteArray ): Map <BytecodeMethodKey , Map <String , Set <Int >>> {
77
+ val lineNumberMapping = HashMap <BytecodeMethodKey , Map <String , Set <Int >>>()
78
+
79
+ ClassReader (bytes).accept(object : ClassVisitor (InlineCodegenUtil .API ) {
80
+ override fun visitMethod (access : Int , name : String? , desc : String? , signature : String? , exceptions : Array <out String >? ): MethodVisitor ? {
81
+ if (name == null || desc == null ) {
82
+ // TODO: check constructors
83
+ return null
84
+ }
82
85
83
- return object : MethodVisitor (Opcodes .ASM5 , null ) {
84
- override fun visitLineNumber (line : Int , start : Label ? ) {
85
- if (start != null ) {
86
- labelsToAllStrings.getOrPutNullable(start.toString(), { LinkedHashSet <Int >() }).add(line)
87
- }
86
+ val methodKey = BytecodeMethodKey (name, desc)
87
+ val methodLinesMapping = HashMap <String , MutableSet <Int >>()
88
+ lineNumberMapping[methodKey] = methodLinesMapping
89
+
90
+ return object : MethodVisitor (Opcodes .ASM5 , null ) {
91
+ override fun visitLineNumber (line : Int , start : Label ? ) {
92
+ if (start != null ) {
93
+ methodLinesMapping.getOrPutNullable(start.toString(), { LinkedHashSet <Int >() }).add(line)
88
94
}
89
95
}
90
96
}
91
- }, ClassReader .SKIP_FRAMES and ClassReader .SKIP_CODE )
92
-
93
- return labelsToAllStrings
94
- }
97
+ }
98
+ }, ClassReader .SKIP_FRAMES and ClassReader .SKIP_CODE )
95
99
96
- val lineMapping = readLineNumberTableMapping(bytes)
97
- return lineMapping.values.firstOrNull { it.contains(lineNumber) }?.last()
100
+ return lineNumberMapping
98
101
}
99
102
100
103
internal fun getOriginalPositionOfInlinedLine (location : Location , project : Project ): Pair <KtFile , Int >? {
@@ -103,14 +106,16 @@ internal fun getOriginalPositionOfInlinedLine(location: Location, project: Proje
103
106
val fileName = location.sourceName()
104
107
val searchScope = GlobalSearchScope .allScope(project)
105
108
106
- val bytes = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ? : return null
107
- val smapData = readDebugInfo(bytes) ? : return null
109
+ val debugInfo = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ? :
110
+ return null
111
+ val smapData = debugInfo.smapData ? : return null
112
+
108
113
return mapStacktraceLineToSource(smapData, lineNumber, project, SourceLineKind .EXECUTED_LINE , searchScope)
109
114
}
110
115
111
116
internal fun findAndReadClassFile (
112
117
fqName : FqName , fileName : String , project : Project , searchScope : GlobalSearchScope ,
113
- fileFilter : (VirtualFile ) -> Boolean ): ByteArray ? {
118
+ fileFilter : (VirtualFile ) -> Boolean ): BytecodeDebugInfo ? {
114
119
val internalName = fqName.asString().replace(' .' , ' /' )
115
120
val jvmClassName = JvmClassName .byInternalName(internalName)
116
121
@@ -151,8 +156,8 @@ private fun inlinedLinesNumbers(
151
156
152
157
val virtualFile = file.virtualFile ? : return listOf ()
153
158
154
- val bytes = readClassFile(project, jvmClassName, virtualFile) ? : return listOf ()
155
- val smapData = readDebugInfo(bytes) ? : return listOf ()
159
+ val debugInfo = readClassFile(project, jvmClassName, virtualFile) ? : return listOf ()
160
+ val smapData = debugInfo.smapData ? : return listOf ()
156
161
157
162
val smap = smapData.kotlinStrata ? : return listOf ()
158
163
@@ -171,5 +176,5 @@ private fun inlinedLinesNumbers(
171
176
@Volatile var emulateDexDebugInTests: Boolean = false
172
177
173
178
fun DebugProcess.isDexDebug () =
174
- (emulateDexDebugInTests && ApplicationManager .getApplication ().isUnitTestMode) ||
179
+ (emulateDexDebugInTests && ApplicationManager .getApplication().isUnitTestMode) ||
175
180
(this .virtualMachineProxy as ? VirtualMachineProxyImpl )?.virtualMachine?.name() == " Dalvik" // TODO: check other machine names
0 commit comments