Skip to content

Commit a129373

Browse files
Merge pull request #10363 from dotty-staging/add-line-sizes-in-TASTy
Fix #6542: Pickle line sizes in TASTy
2 parents 928e01a + 361a680 commit a129373

File tree

11 files changed

+81
-20
lines changed

11 files changed

+81
-20
lines changed

compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala

+15-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,21 @@ class PositionPickler(
3232
(addrDelta << 3) | (toInt(hasStartDelta) << 2) | (toInt(hasEndDelta) << 1) | toInt(hasPoint)
3333
}
3434

35-
def picklePositions(roots: List[Tree], warnings: mutable.ListBuffer[String]): Unit = {
35+
def picklePositions(source: SourceFile, roots: List[Tree], warnings: mutable.ListBuffer[String]): Unit = {
36+
/** Pickle the number of lines followed by the length of each line */
37+
def pickleLineOffsets(): Unit = {
38+
val content = source.content()
39+
buf.writeNat(content.count(_ == '\n') + 1) // number of lines
40+
var lastIndex = content.indexOf('\n', 0)
41+
buf.writeNat(lastIndex) // size of first line
42+
while lastIndex != -1 do
43+
val nextIndex = content.indexOf('\n', lastIndex + 1)
44+
val end = if nextIndex != -1 then nextIndex else content.length
45+
buf.writeNat(end - lastIndex - 1) // size of the next line
46+
lastIndex = nextIndex
47+
}
48+
pickleLineOffsets()
49+
3650
var lastIndex = 0
3751
var lastSpan = Span(0, 0)
3852
def pickleDeltas(index: Int, span: Span) = {

compiler/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala

+13
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,20 @@ import Names.TermName
1515
class PositionUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName) {
1616
import reader._
1717

18+
private var myLineSizes: Array[Int] = _
1819
private var mySpans: util.HashMap[Addr, Span] = _
1920
private var mySourcePaths: util.HashMap[Addr, String] = _
2021
private var isDefined = false
2122

2223
def ensureDefined(): Unit = {
2324
if (!isDefined) {
25+
val lines = readNat()
26+
myLineSizes = new Array[Int](lines)
27+
var i = 0
28+
while i < lines do
29+
myLineSizes(i) += readNat()
30+
i += 1
31+
2432
mySpans = util.HashMap[Addr, Span]()
2533
mySourcePaths = util.HashMap[Addr, String]()
2634
var curIndex = 0
@@ -60,6 +68,11 @@ class PositionUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName) {
6068
mySourcePaths
6169
}
6270

71+
private[tasty] def lineSizes: Array[Int] = {
72+
ensureDefined()
73+
myLineSizes
74+
}
75+
6376
def spanAt(addr: Addr): Span = spans.getOrElse(addr, NoSpan)
6477
def sourcePathAt(addr: Addr): String = sourcePaths.getOrElse(addr, "")
6578
}

compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala

+7-2
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,14 @@ class TastyPrinter(bytes: Array[Byte]) {
139139
private val sb: StringBuilder = new StringBuilder
140140

141141
def unpickle(reader: TastyReader, tastyName: NameTable): String = {
142+
val posUnpickler = new PositionUnpickler(reader, tastyName)
142143
sb.append(s" ${reader.endAddr.index - reader.currentAddr.index}")
143-
val spans = new PositionUnpickler(reader, tastyName).spans
144-
sb.append(s" position bytes:\n")
144+
sb.append(" position bytes:\n")
145+
val lineSizes = posUnpickler.lineSizes
146+
sb.append(s" lines: ${lineSizes.length}\n")
147+
sb.append(posUnpickler.lineSizes.mkString(" line sizes: ", ", ", "\n"))
148+
sb.append(" positions:\n")
149+
val spans = posUnpickler.spans
145150
val sorted = spans.toSeq.sortBy(_._1.index)
146151
for ((addr, pos) <- sorted) {
147152
sb.append(treeStr("%10d".format(addr.index)))

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

+6-1
Original file line numberDiff line numberDiff line change
@@ -1363,8 +1363,13 @@ class TreeUnpickler(reader: TastyReader,
13631363
def sourceChangeContext(addr: Addr = currentAddr)(using Context): Context = {
13641364
val path = sourcePathAt(addr)
13651365
if (path.nonEmpty) {
1366+
val sourceFile = ctx.getSource(path)
1367+
posUnpicklerOpt match
1368+
case Some(posUnpickler) =>
1369+
sourceFile.setLineIndicesFromLineSizes(posUnpickler.lineSizes)
1370+
case _ =>
13661371
pickling.println(i"source change at $addr: $path")
1367-
ctx.withSource(ctx.getSource(path))
1372+
ctx.withSource(sourceFile)
13681373
}
13691374
else ctx
13701375
}

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ object PickledQuotes {
168168
if tree.span.exists then
169169
val positionWarnings = new mutable.ListBuffer[String]()
170170
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots)
171-
.picklePositions(tree :: Nil, positionWarnings)
171+
.picklePositions(ctx.compilationUnit.source, tree :: Nil, positionWarnings)
172172
positionWarnings.foreach(report.warning(_))
173173

174174
val pickled = pickler.assembleParts()

compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ trait MessageRendering {
147147
val sb = mutable.StringBuilder()
148148
val posString = posStr(pos, diagnosticLevel, msg)
149149
if (posString.nonEmpty) sb.append(posString).append(EOL)
150-
if (pos.exists && pos.source.file.exists) {
150+
if (pos.exists) {
151151
val pos1 = pos.nonInlined
152152
val (srcBefore, srcAfter, offset) = sourceLines(pos1, diagnosticLevel)
153153
val marker = columnMarker(pos1, offset, diagnosticLevel)

compiler/src/dotty/tools/dotc/transform/Pickler.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class Pickler extends Phase {
7373
treePkl.compactify()
7474
if tree.span.exists then
7575
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots)
76-
.picklePositions(tree :: Nil, positionWarnings)
76+
.picklePositions(unit.source, tree :: Nil, positionWarnings)
7777

7878
if !ctx.settings.YdropComments.value then
7979
new CommentPickler(pickler, treePkl.buf.addrOfTree, treePkl.docString)

compiler/src/dotty/tools/dotc/util/SourceFile.scala

+21-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
8282

8383
def apply(idx: Int): Char = content().apply(idx)
8484

85-
def length: Int = content().length
85+
def length: Int =
86+
if lineIndicesCache ne null then lineIndicesCache.last
87+
else content().length
8688

8789
/** true for all source files except `NoSource` */
8890
def exists: Boolean = true
@@ -105,7 +107,8 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
105107
def positionInUltimateSource(position: SourcePosition): SourcePosition =
106108
SourcePosition(underlying, position.span shift start)
107109

108-
private def calculateLineIndices(cs: Array[Char]) = {
110+
private def calculateLineIndicesFromContents() = {
111+
val cs = content()
109112
val buf = new ArrayBuffer[Int]
110113
buf += 0
111114
var i = 0
@@ -120,7 +123,22 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
120123
buf += cs.length // sentinel, so that findLine below works smoother
121124
buf.toArray
122125
}
123-
private lazy val lineIndices: Array[Int] = calculateLineIndices(content())
126+
127+
private var lineIndicesCache: Array[Int] = _
128+
private def lineIndices: Array[Int] =
129+
if lineIndicesCache eq null then
130+
lineIndicesCache = calculateLineIndicesFromContents()
131+
lineIndicesCache
132+
def setLineIndicesFromLineSizes(sizes: Array[Int]): Unit =
133+
val lines = sizes.length
134+
val indices = new Array[Int](lines + 1)
135+
var i = 0
136+
val penultimate = lines - 1
137+
while i < penultimate do
138+
indices(i + 1) = indices(i) + sizes(i) + 1 // `+1` for the '\n' at the end of the line
139+
i += 1
140+
indices(lines) = indices(penultimate) + sizes(penultimate) // last line does not end with '\n'
141+
lineIndicesCache = indices
124142

125143
/** Map line to offset of first character in line */
126144
def lineToOffset(index: Int): Int = lineIndices(index)

compiler/src/dotty/tools/dotc/util/SourcePosition.scala

+6-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extends SrcPos, interfaces.SourcePosition, Showable {
2525

2626
def point: Int = span.point
2727

28-
def line: Int = if (source.length != 0) source.offsetToLine(point) else -1
28+
def line: Int = source.offsetToLine(point)
2929

3030
/** Extracts the lines from the underlying source file as `Array[Char]`*/
3131
def linesSlice: Array[Char] =
@@ -45,16 +45,16 @@ extends SrcPos, interfaces.SourcePosition, Showable {
4545
def beforeAndAfterPoint: (List[Int], List[Int]) =
4646
lineOffsets.partition(_ <= point)
4747

48-
def column: Int = if (source.content().length != 0) source.column(point) else -1
48+
def column: Int = source.column(point)
4949

5050
def start: Int = span.start
51-
def startLine: Int = if (source.content().length != 0) source.offsetToLine(start) else -1
52-
def startColumn: Int = if (source.content().length != 0) source.column(start) else -1
51+
def startLine: Int = source.offsetToLine(start)
52+
def startColumn: Int = source.column(start)
5353
def startColumnPadding: String = source.startColumnPadding(start)
5454

5555
def end: Int = span.end
56-
def endLine: Int = if (source.content().length != 0) source.offsetToLine(end) else -1
57-
def endColumn: Int = if (source.content().length != 0) source.column(end) else -1
56+
def endLine: Int = source.offsetToLine(end)
57+
def endColumn: Int = source.column(end)
5858

5959
def withOuter(outer: SourcePosition): SourcePosition = SourcePosition(source, span, outer)
6060
def withSpan(range: Span) = SourcePosition(source, range, outer)

project/scripts/cmdTests

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ cp tests/neg/i6371/B_2.scala $OUT/B.scala
5252
rm $OUT/A.scala
5353
"$SBT" "scalac -classpath $OUT1 -d $OUT1 $OUT/B.scala" > "$tmp" 2>&1 || echo "ok"
5454
grep -qe "B.scala:2:7" "$tmp"
55+
grep -qe "This location contains code that was inlined from A.scala:3" "$tmp"
5556

5657
echo "testing -Ythrough-tasty"
5758
clear_out "$OUT"

tasty/src/dotty/tools/tasty/TastyFormat.scala

+9-4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Macro-format:
5050
Note: Unqualified names in the name table are strings. The context decides whether a name is
5151
a type-name or a term-name. The same string can represent both.
5252
53+
5354
Standard-Section: "ASTs" TopLevelStat*
5455
5556
TopLevelStat = PACKAGE Length Path TopLevelStat* -- package path { topLevelStats }
@@ -226,7 +227,10 @@ Note: Tree tags are grouped into 5 categories that determine what follows, and t
226227
Category 4 (tags 110-127): tag Nat AST
227228
Category 5 (tags 128-255): tag Length <payload>
228229
229-
Standard-Section: "Positions" Assoc*
230+
231+
Standard-Section: "Positions" LinesSizes Assoc*
232+
233+
LinesSizes = Nat Nat* // Number of lines followed by the size of each line not counting the trailing `\n`
230234
231235
Assoc = Header offset_Delta? offset_Delta? point_Delta?
232236
| SOURCE nameref_Int
@@ -244,7 +248,8 @@ Standard-Section: "Positions" Assoc*
244248
245249
All elements of a position section are serialized as Ints
246250
247-
Standard-Section: "Comments" Comment*
251+
252+
Standard Section: "Comments" Comment*
248253
249254
Comment = Length Bytes LongInt // Raw comment's bytes encoded as UTF-8, followed by the comment's coordinates.
250255
@@ -254,8 +259,8 @@ Standard-Section: "Comments" Comment*
254259
object TastyFormat {
255260

256261
final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
257-
val MajorVersion: Int = 25
258-
val MinorVersion: Int = 1
262+
val MajorVersion: Int = 26
263+
val MinorVersion: Int = 0
259264

260265
final val ASTsSection = "ASTs"
261266
final val PositionsSection = "Positions"

0 commit comments

Comments
 (0)