1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
/* __ *\
** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
** /____/\___/_/ |_/____/_/ | |__/ /____/ **
** |/____/ **
\* */
package scala.scalajs.tools.sourcemap
import scala.scalajs.tools.classpath._
import com.google.debugging.sourcemap._
class SourceMapper(classpath: CompleteClasspath) {
def map(ste: StackTraceElement, columnNumber: Int): StackTraceElement = {
val mapped = for {
sourceMap <- findSourceMap(ste.getFileName)
} yield map(ste, columnNumber, sourceMap)
mapped.getOrElse(ste)
}
def map(ste: StackTraceElement, columnNumber: Int,
sourceMap: String): StackTraceElement = {
val sourceMapConsumer =
SourceMapConsumerFactory
.parse(sourceMap)
.asInstanceOf[SourceMapConsumerV3]
/* **Attention**
* - StackTrace positions are 1-based
* - SourceMapConsumer.getMappingForLine() is 1-based
*/
val lineNumber = ste.getLineNumber
val column =
if (columnNumber == -1) getFirstColumn(sourceMapConsumer, lineNumber)
else columnNumber
val originalMapping =
sourceMapConsumer.getMappingForLine(lineNumber, column)
if (originalMapping != null) {
new StackTraceElement(
ste.getClassName,
ste.getMethodName,
originalMapping.getOriginalFile,
originalMapping.getLineNumber)
} else ste
}
/** both `lineNumber` and the resulting column are 1-based */
private def getFirstColumn(sourceMapConsumer: SourceMapConsumerV3,
lineNumber1Based: Int) = {
/* **Attention**
* - SourceMapConsumerV3.EntryVisitor is 0-based!!!
*/
val lineNumber = lineNumber1Based - 1
var column: Option[Int] = None
sourceMapConsumer.visitMappings(
new SourceMapConsumerV3.EntryVisitor {
def visit(sourceName: String,
symbolName: String,
sourceStartPosition: FilePosition,
startPosition: FilePosition,
endPosition: FilePosition): Unit =
if (!column.isDefined && startPosition.getLine == lineNumber)
column = Some(startPosition.getColumn)
})
val column0Based = column.getOrElse(0)
column0Based + 1
}
private def findSourceMap(path: String) = {
val candidates = classpath.allCode.filter(_.path == path)
if (candidates.size != 1) None // better no sourcemap than a wrong one
else candidates.head.sourceMap
}
}
|