Skip to content

Commit 333ec27

Browse files
committed
Set the positions of inlined trees wehn read form Tasty
This required a major change in the way positions are handled, as the previous scheme did not allow to read the positions of arbitrary subtrees selectively. Fortunately, it's altogether a major simplification. Also, this fixed a bug in the previous scheme, where positions were generated before compactification, resulting in addresses being wrong.
1 parent 688cc89 commit 333ec27

10 files changed

+107
-162
lines changed

src/dotty/tools/dotc/FromTasty.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ object FromTasty extends Driver {
8686
case info: ClassfileLoader =>
8787
info.load(clsd) match {
8888
case Some(unpickler: DottyUnpickler) =>
89-
val List(unpickled) = unpickler.body(readPositions = true)
89+
val List(unpickled) = unpickler.body(ctx.addMode(Mode.ReadPositions))
9090
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq()))
9191
unit1.tpdTree = unpickled
9292
unit1.unpicklers += (clsd.classSymbol -> unpickler.unpickler)

src/dotty/tools/dotc/core/Mode.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,8 @@ object Mode {
8989
*/
9090
val AllowLambdaWildcardApply = newMode(15, "AllowHKApplyToWildcards")
9191

92+
/** Read original positions when unpickling from TASTY */
93+
val ReadPositions = newMode(16, "ReadPositions")
94+
9295
val PatternOrType = Pattern | Type
9396
}

src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,24 @@ import dotty.tools.dotc.ast.tpd
88
import TastyUnpickler._, TastyBuffer._
99
import util.Positions._
1010
import util.{SourceFile, NoSource}
11-
import PositionUnpickler._
1211
import Annotations.Annotation
12+
import core.Mode
1313
import classfile.ClassfileParser
1414

1515
object DottyUnpickler {
1616

1717
/** Exception thrown if classfile is corrupted */
1818
class BadSignature(msg: String) extends RuntimeException(msg)
1919

20-
class TreeSectionUnpickler extends SectionUnpickler[TreeUnpickler]("ASTs") {
20+
class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler])
21+
extends SectionUnpickler[TreeUnpickler]("ASTs") {
2122
def unpickle(reader: TastyReader, tastyName: TastyName.Table) =
22-
new TreeUnpickler(reader, tastyName)
23+
new TreeUnpickler(reader, tastyName, posUnpickler)
2324
}
2425

25-
class PositionsSectionUnpickler extends SectionUnpickler[(Position, AddrToPosition)]("Positions") {
26+
class PositionsSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") {
2627
def unpickle(reader: TastyReader, tastyName: TastyName.Table) =
27-
new PositionUnpickler(reader).unpickle()
28+
new PositionUnpickler(reader)
2829
}
2930
}
3031

@@ -36,21 +37,17 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded {
3637
import DottyUnpickler._
3738

3839
val unpickler = new TastyUnpickler(bytes)
39-
private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler).get
40+
private val posUnpicklerOpt = unpickler.unpickle(new PositionsSectionUnpickler)
41+
private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler(posUnpicklerOpt)).get
4042

4143
/** Enter all toplevel classes and objects into their scopes
4244
* @param roots a set of SymDenotations that should be overwritten by unpickling
4345
*/
4446
def enter(roots: Set[SymDenotation])(implicit ctx: Context): Unit =
4547
treeUnpickler.enterTopLevel(roots)
4648

47-
/** The unpickled trees, and the source file they come from
48-
* @param readPositions if true, trees get decorated with position information.
49-
*/
50-
def body(readPositions: Boolean = false)(implicit ctx: Context): List[Tree] = {
51-
if (readPositions)
52-
for ((totalRange, positions) <- unpickler.unpickle(new PositionsSectionUnpickler))
53-
treeUnpickler.usePositions(totalRange, positions)
49+
/** The unpickled trees, and the source file they come from. */
50+
def body(implicit ctx: Context): List[Tree] = {
5451
treeUnpickler.unpickle()
5552
}
5653
}

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

Lines changed: 38 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package dotc
33
package core
44
package tasty
55

6-
import ast.tpd._
6+
import ast._
7+
import ast.Trees._
78
import ast.Trees.WithLazyField
89
import TastyFormat._
910
import core._
@@ -12,64 +13,47 @@ import collection.mutable
1213
import TastyBuffer._
1314
import util.Positions._
1415

15-
object PositionPickler {
16-
17-
trait DeferredPosition {
18-
var parentPos: Position = NoPosition
19-
}
20-
21-
def traverse(x: Any, parentPos: Position, op: (Tree, Position) => Unit)(implicit ctx: Context): Unit =
22-
if (parentPos.exists)
23-
x match {
24-
case x: Tree @unchecked =>
25-
op(x, parentPos)
26-
x match {
27-
case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos, op)
28-
case _ =>
29-
}
30-
traverse(x.productIterator, x.pos, op)
31-
case x: DeferredPosition =>
32-
x.parentPos = parentPos
33-
case xs: TraversableOnce[_] =>
34-
xs.foreach(traverse(_, parentPos, op))
35-
case _ =>
36-
}
37-
}
38-
import PositionPickler._
39-
40-
class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) {
16+
class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr]) {
4117
val buf = new TastyBuffer(5000)
4218
pickler.newSection("Positions", buf)
4319
import buf._
20+
import ast.tpd._
21+
22+
def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean) = {
23+
def toInt(b: Boolean) = if (b) 1 else 0
24+
(addrDelta << 2) | (toInt(hasStartDelta) << 1) | toInt(hasEndDelta)
25+
}
4426

45-
def picklePositions(roots: List[Tree], totalRange: Position)(implicit ctx: Context) = {
27+
def picklePositions(roots: List[Tree])(implicit ctx: Context) = {
4628
var lastIndex = 0
47-
def record(tree: Tree, parentPos: Position): Unit =
48-
if (tree.pos.exists) {
49-
def msg = s"failure to pickle $tree at ${tree.pos}, parent = $parentPos"
50-
val endPos = tree.pos.end min parentPos.end
51-
// end positions can be larger than their parents
52-
// e.g. in the case of synthetic empty ranges, which are placed at the next token after
53-
// the current construct.
54-
val endDelta = endPos - parentPos.end
55-
val startPos =
56-
if (endDelta == 0) tree.pos.start max parentPos.start else tree.pos.start min endPos
57-
// Since end positions are corrected above, start positions have to follow suit.
58-
val startDelta = startPos - parentPos.start
59-
if (startDelta != 0 || endDelta != 0)
60-
for (addr <- addrOfTree(tree)) {
61-
buf.writeInt(addr.index - lastIndex)
62-
lastIndex = addr.index
63-
if (startDelta != 0) buf.writeInt(startDelta)
64-
if (endDelta != 0) {
65-
assert(endDelta < 0, msg)
66-
buf.writeInt(endDelta)
67-
} else
68-
assert(startDelta >= 0, msg)
29+
var lastPos = Position(0, 0)
30+
def pickleDeltas(index: Int, pos: Position) = {
31+
val addrDelta = index - lastIndex
32+
val startDelta = pos.start - lastPos.start
33+
val endDelta = pos.end - lastPos.end
34+
buf.writeInt(header(addrDelta, startDelta != 0, endDelta != 0))
35+
if (startDelta != 0) buf.writeInt(startDelta)
36+
if (endDelta != 0) buf.writeInt(endDelta)
37+
lastIndex = index
38+
lastPos = pos
39+
}
40+
def traverse(x: Any, parentPos: Position): Unit = x match {
41+
case x: Tree @unchecked =>
42+
if (x.pos.exists && x.pos.toSynthetic != parentPos.toSynthetic) {
43+
addrOfTree(x) match {
44+
case Some(addr) => pickleDeltas(addr.index, x.pos)
45+
case _ =>
6946
}
70-
}
71-
72-
buf.writeNat(totalRange.end)
73-
traverse(roots, totalRange, record)
47+
}
48+
x match {
49+
case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos)
50+
case _ =>
51+
}
52+
traverse(x.productIterator, x.pos)
53+
case xs: TraversableOnce[_] =>
54+
xs.foreach(traverse(_, parentPos))
55+
case _ =>
56+
}
57+
traverse(roots, NoPosition)
7458
}
7559
}

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

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,31 @@ package tasty
66

77
import util.Positions._
88
import collection.mutable
9-
import TastyBuffer.Addr
10-
11-
object PositionUnpickler {
12-
type AddrToPosition = mutable.HashMap[Addr, Position]
13-
}
9+
import TastyBuffer.{Addr, NoAddr}
1410

1511
/** Unpickler for tree positions */
1612
class PositionUnpickler(reader: TastyReader) {
17-
import PositionUnpickler._
1813
import reader._
1914

20-
def unpickle(): (Position, AddrToPosition) = {
21-
val positions = new mutable.HashMap[Addr, Position] // Dotty deviation: Can't use new AddrToPosition here. TODO: fix this!
22-
val sourceLength = readNat()
23-
def readDelta() = if (isAtEnd) 0 else readInt()
24-
var curIndex: Addr = Addr(readDelta())
15+
private[tasty] lazy val positions = {
16+
val positions = new mutable.HashMap[Addr, Position]
17+
var curIndex = 0
18+
var curStart = 0
19+
var curEnd = 0
2520
while (!isAtEnd) {
26-
val delta1 = readDelta()
27-
val delta2 = readDelta()
28-
val (startDelta, endDelta, indexDelta) =
29-
if (delta2 <= 0) (delta1, -delta2, readDelta())
30-
else if (delta1 < 0) (0, -delta1, delta2)
31-
else (delta1, 0, delta2)
32-
positions(curIndex) = Position(startDelta, endDelta, startDelta)
33-
// make non-synthetic position; will be made synthetic by normalization.
34-
curIndex += indexDelta
21+
val header = readInt()
22+
val addrDelta = header >> 2
23+
val hasStart = (header & 2) != 0
24+
val hasEnd = (header & 1) != 0
25+
curIndex += addrDelta
26+
assert(curIndex >= 0)
27+
if (hasStart) curStart += readInt()
28+
if (hasEnd) curEnd += readInt()
29+
positions(Addr(curIndex)) = Position(curStart, curEnd)
3530
}
36-
(Position(0, sourceLength), positions)
31+
positions
3732
}
33+
34+
def posAt(addr: Addr) = positions.getOrElse(addr, NoPosition)
3835
}
36+

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

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -185,21 +185,16 @@ Note: Tree tags are grouped into 5 categories that determine what follows, and t
185185
Category 4 (tags 112-127): tag Nat AST
186186
Category 5 (tags 128-255): tag Length <payload>
187187
188-
Standard Section: "Positions" sourceLength_Nat Assoc*
189-
190-
Assoc = addr_Delta offset_Delta offset_Delta?
191-
// addr_Delta :
192-
// Difference of address to last recorded node.
193-
// All but the first addr_Deltas are > 0, the first is >= 0.
194-
// 2nd offset_Delta:
195-
// Difference of end offset of addressed node vs parent node. Always <= 0
196-
// 1st offset Delta, if delta >= 0 or 2nd offset delta exists
197-
// Difference of start offset of addressed node vs parent node.
198-
// 1st offset Delta, if delta < 0 and 2nd offset delta does not exist:
199-
// Difference of end offset of addressed node vs parent node.
200-
// Offsets and addresses are difference encoded.
188+
Standard Section: "Positions" Assoc*
189+
190+
Assoc = Header offset_Delta? offset_Delta?
191+
Header = addr_Delta + // in one Nat: difference of address to last recorded node << 2 +
192+
hasStartDiff + // one bit indicating whether there follows a start address delta << 1
193+
hasEndDiff // one bit indicating whether there follows an end address delta
201194
// Nodes which have the same positions as their parents are omitted.
202-
Delta = Int // Difference between consecutive offsets / tree addresses,
195+
// offset_Deltas give difference of start/end offset wrt to the
196+
// same offset in the previously recorded node (or 0 for the first recorded node)
197+
Delta = Int // Difference between consecutive offsets,
203198
204199
**************************************************************************************/
205200

src/dotty/tools/dotc/core/tasty/TastyPickler.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ class TastyPickler {
3131
sections += ((nameBuffer.nameIndex(name), buf))
3232

3333
def assembleParts(): Array[Byte] = {
34-
treePkl.compactify()
3534
def lengthWithLength(buf: TastyBuffer) = {
3635
buf.assemble()
3736
buf.length + natSize(buf.length)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
113113
class PositionSectionUnpickler extends SectionUnpickler[Unit]("Positions") {
114114
def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = {
115115
print(s"${reader.endAddr.index - reader.currentAddr.index}")
116-
val (totalRange, positions) = new PositionUnpickler(reader).unpickle()
117-
println(s" position bytes in $totalRange:")
116+
val positions = new PositionUnpickler(reader).positions
117+
println(s" position bytes:")
118118
val sorted = positions.toSeq.sortBy(_._1.index)
119119
for ((addr, pos) <- sorted) println(s"${addr.index}: ${offsetToInt(pos.start)} .. ${pos.end}")
120120
}

0 commit comments

Comments
 (0)