@@ -34,16 +34,17 @@ private[internal] case class ClassFile(
34
34
private class ClassEntryLookUp (
35
35
val entry : ClassEntry ,
36
36
fqcnToClassFile : Map [String , ClassFile ],
37
- sourceUriToSourceFile : Map [SourceFileKey , SourceFile ],
38
- sourceUriToClassFiles : Map [SourceFileKey , Seq [ClassFile ]],
37
+ sourceUriToSourceFile : Map [SanitizedUri , SourceFile ],
38
+ sourceUriToClassFiles : Map [SanitizedUri , Seq [ClassFile ]],
39
39
classNameToSourceFile : Map [String , SourceFile ],
40
40
missingSourceFileClassFiles : Seq [ClassFile ],
41
41
val orphanClassFiles : Seq [ClassFile ],
42
42
logger : Logger
43
43
) {
44
- private val cachedSourceLines = mutable.Map [SourceLineKey , Seq [ClassFile ]]()
44
+ private val sourceLineCache = mutable.Map .empty[SourceLine , Seq [ClassFile ]]
45
+ private val scalaSigCache = mutable.Map .empty[String , Option [ScalaSig ]]
45
46
46
- def sources : Iterable [SourceFileKey ] = sourceUriToSourceFile.keys
47
+ def sources : Iterable [SanitizedUri ] = sourceUriToSourceFile.keys
47
48
def fullyQualifiedNames : Iterable [String ] = {
48
49
classNameToSourceFile.keys ++
49
50
orphanClassFiles.map(_.fullyQualifiedName) ++
@@ -54,75 +55,64 @@ private class ClassEntryLookUp(
54
55
fullyQualifiedNames.groupBy[String ](NameTransformer .scalaClassName)
55
56
56
57
def getFullyQualifiedClassName (
57
- sourceKey : SourceFileKey ,
58
+ sourceUri : SanitizedUri ,
58
59
lineNumber : Int
59
60
): Option [String ] = {
60
- val line = SourceLineKey (sourceKey , lineNumber)
61
+ val sourceLine = SourceLine (sourceUri , lineNumber)
61
62
62
- if (! cachedSourceLines .contains(line )) {
63
+ if (! sourceLineCache .contains(sourceLine )) {
63
64
// read and cache line numbers from class files
64
65
sourceUriToClassFiles
65
- .getOrElse(sourceKey , Nil )
66
+ .getOrElse(sourceUri , Nil )
66
67
.groupBy(_.classSystem)
67
68
.foreach { case (classSystem, classFiles) =>
68
69
classSystem
69
- .within((_, root) => loadLineNumbers( root, classFiles, sourceKey ))
70
+ .within((_, root) => classFiles.foreach(loadLines( root, _, sourceUri) ))
70
71
.warnFailure(logger, s " Cannot load line numbers in ${classSystem.name}" )
71
72
}
72
73
}
73
74
74
- cachedSourceLines
75
- .get(line )
75
+ sourceLineCache
76
+ .get(sourceLine )
76
77
.map { classFiles =>
77
78
// The same breakpoint can stop in different classes
78
79
// We choose the one with the smallest name
79
80
classFiles.map(_.fullyQualifiedName).minBy(_.length)
80
81
}
81
82
}
82
83
83
- private def loadLineNumbers (
84
- root : Path ,
85
- classFiles : Seq [ClassFile ],
86
- sourceKey : SourceFileKey
87
- ): Unit = {
88
- for (classFile <- classFiles) {
89
- val path = root.resolve(classFile.relativePath)
90
- val inputStream = Files .newInputStream(path)
91
- try {
92
- val reader = new ClassReader (inputStream)
93
-
94
- val lineNumbers = mutable.Buffer [Int ]()
95
-
96
- val visitor = new ClassVisitor (Opcodes .ASM9 ) {
97
- override def visitMethod (
98
- access : Int ,
99
- name : String ,
100
- desc : String ,
101
- signature : String ,
102
- exceptions : Array [String ]
103
- ): MethodVisitor = {
104
- new MethodVisitor (Opcodes .ASM9 ) {
105
- override def visitLineNumber (line : Int , start : Label ): Unit = {
106
- lineNumbers.append(line)
107
- }
108
- }
84
+ private def loadLines (root : Path , classFile : ClassFile , sourceUri : SanitizedUri ): Unit = {
85
+ val lines = mutable.Set [Int ]()
86
+ val path = root.resolve(classFile.relativePath)
87
+ val inputStream = Files .newInputStream(path)
88
+ try {
89
+ val reader = new ClassReader (inputStream)
90
+ val visitor = new ClassVisitor (Opcodes .ASM9 ) {
91
+ override def visitMethod (
92
+ access : Int ,
93
+ name : String ,
94
+ desc : String ,
95
+ signature : String ,
96
+ exceptions : Array [String ]
97
+ ): MethodVisitor =
98
+ new MethodVisitor (Opcodes .ASM9 ) {
99
+ override def visitLineNumber (line : Int , start : Label ): Unit =
100
+ lines += line
109
101
}
110
- }
111
- reader.accept(visitor, 0 )
112
-
113
- for (n <- lineNumbers) {
114
- val line = SourceLineKey (sourceKey, n)
115
- cachedSourceLines.update(
116
- line,
117
- cachedSourceLines.getOrElse(line, Seq .empty) :+ classFile
118
- )
119
- }
120
- } finally inputStream.close()
102
+ }
103
+ reader.accept(visitor, 0 )
104
+ } finally inputStream.close()
105
+ for (line <- lines) {
106
+ val sourceLine = SourceLine (sourceUri, line)
107
+ sourceLineCache.update(
108
+ sourceLine,
109
+ sourceLineCache.getOrElse(sourceLine, Seq .empty) :+ classFile
110
+ )
121
111
}
122
112
}
123
113
124
114
def getSourceContent (sourceUri : URI ): Option [String ] =
125
- sourceUriToSourceFile.get(SourceFileKey (sourceUri)).flatMap(readSourceContent(_, logger))
115
+ sourceUriToSourceFile.get(SanitizedUri (sourceUri)).flatMap(readSourceContent(_, logger))
126
116
127
117
def getSourceFileURI (fqcn : String ): Option [URI ] =
128
118
classNameToSourceFile.get(fqcn).map(_.uri)
@@ -131,7 +121,7 @@ private class ClassEntryLookUp(
131
121
getSourceFileURI(fqcn).flatMap(getSourceContent)
132
122
133
123
def getClassFiles (sourceUri : URI ): Seq [ClassFile ] =
134
- sourceUriToClassFiles.get(SourceFileKey (sourceUri)).getOrElse(Seq .empty)
124
+ sourceUriToClassFiles.get(SanitizedUri (sourceUri)).getOrElse(Seq .empty)
135
125
136
126
def getClassFile (fqcn : String ): Option [ClassFile ] =
137
127
fqcnToClassFile.get(fqcn)
@@ -156,7 +146,7 @@ private class ClassEntryLookUp(
156
146
else scalaSigs.headOption
157
147
}
158
148
159
- fromClass.orElse(fromSource)
149
+ scalaSigCache.getOrElseUpdate(fqcn, fromClass.orElse(fromSource) )
160
150
}
161
151
}
162
152
@@ -182,21 +172,21 @@ private object ClassEntryLookUp {
182
172
classFiles.map(c => (c.fullyQualifiedName, c)).toMap
183
173
184
174
val sourceFileToRoot = sourceLookUps.flatMap(l => l.sourceFiles.map(f => (f -> l.root))).toMap
185
- val sourceUriToSourceFile = sourceLookUps.flatMap(_.sourceFiles).map(f => (SourceFileKey (f.uri), f)).toMap
175
+ val sourceUriToSourceFile = sourceLookUps.flatMap(_.sourceFiles).map(f => (SanitizedUri (f.uri), f)).toMap
186
176
val sourceNameToSourceFile = sourceLookUps.flatMap(_.sourceFiles).groupBy(f => f.fileName)
187
177
188
178
val classNameToSourceFile = mutable.Map [String , SourceFile ]()
189
- val sourceUriToClassFiles = mutable.Map [SourceFileKey , Seq [ClassFile ]]()
179
+ val sourceUriToClassFiles = mutable.Map [SanitizedUri , Seq [ClassFile ]]()
190
180
val orphanClassFiles = mutable.Buffer [ClassFile ]()
191
181
val missingSourceFileClassFiles = mutable.Buffer [ClassFile ]()
192
182
193
183
for (classFile <- classFiles) {
194
184
def recordSourceFile (sourceFile : SourceFile ): Unit = {
195
185
classNameToSourceFile.put(classFile.fullyQualifiedName, sourceFile)
196
186
sourceUriToClassFiles.update(
197
- SourceFileKey (sourceFile.uri),
187
+ SanitizedUri (sourceFile.uri),
198
188
sourceUriToClassFiles.getOrElse(
199
- SourceFileKey (sourceFile.uri),
189
+ SanitizedUri (sourceFile.uri),
200
190
Seq .empty
201
191
) :+ classFile
202
192
)
@@ -360,12 +350,12 @@ private object ClassEntryLookUp {
360
350
/**
361
351
* On a case-insensitive system we need to sanitize all URIs to use them as Map keys.
362
352
*/
363
- private case class SourceFileKey ( sanitizeUri : URI )
353
+ private class SanitizedUri ( val uri : URI ) extends AnyVal
364
354
365
- private object SourceFileKey {
355
+ private object SanitizedUri {
366
356
private val isCaseSensitiveFileSystem = Properties .isWin || Properties .isMac
367
357
368
- def apply (uri : URI ): SourceFileKey = {
358
+ def apply (uri : URI ): SanitizedUri = {
369
359
val sanitizeUri : URI =
370
360
if (isCaseSensitiveFileSystem) {
371
361
uri.getScheme match {
@@ -379,8 +369,8 @@ private object SourceFileKey {
379
369
case _ => uri
380
370
}
381
371
} else uri
382
- new SourceFileKey (sanitizeUri)
372
+ new SanitizedUri (sanitizeUri)
383
373
}
384
374
}
385
375
386
- private case class SourceLineKey (sourceFile : SourceFileKey , lineNumber : Int )
376
+ private case class SourceLine (sourceFile : SanitizedUri , lineNumber : Int )
0 commit comments