package dotty.tools
package dotc
package core
package pickling
import ast.tpd._
import ast.Trees.WithLazyField
import PickleFormat._
import core._
import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._
import collection.mutable
import TastyBuffer._
import util.Positions._
object PositionPickler {
trait DeferredPosition {
var parentPos: Position = NoPosition
}
def traverse(x: Any, parentPos: Position, op: (Tree, Position) => Unit)(implicit ctx: Context): Unit =
if (parentPos.exists)
x match {
case x: Tree @unchecked =>
op(x, parentPos)
x match {
case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos, op)
case _ =>
}
traverse(x.productIterator, x.pos, op)
case x: DeferredPosition =>
x.parentPos = parentPos
case xs: TraversableOnce[_] =>
xs.foreach(traverse(_, parentPos, op))
case _ =>
}
}
import PositionPickler._
class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) {
val buf = new TastyBuffer(100000)
pickler.newSection("Positions", buf)
import buf._
def picklePositions(roots: List[Tree], totalRange: Position)(implicit ctx: Context) = {
var lastIndex = 0
def record(tree: Tree, parentPos: Position): Unit =
if (tree.pos.exists) {
def msg = s"failure to pickle $tree at ${tree.pos}, parent = $parentPos"
val endPos = tree.pos.end min parentPos.end
// end positions can be larger than their parents
// e.g. in the case of synthetic empty ranges, which are placed at the next token after
// the current construct.
val endDelta = endPos - parentPos.end
val startPos =
if (endDelta == 0) tree.pos.start max parentPos.start else tree.pos.start min endPos
// Since end positions are corrected above, start positions have to follow suit.
val startDelta = startPos - parentPos.start
if (startDelta != 0 || endDelta != 0)
for (addr <- addrOfTree(tree)) {
buf.writeInt(addr.index - lastIndex)
lastIndex = addr.index
if (startDelta != 0) buf.writeInt(startDelta)
if (endDelta != 0) {
assert(endDelta < 0, msg)
buf.writeInt(endDelta)
} else
assert(startDelta >= 0, msg)
}
}
buf.writeNat(totalRange.end)
traverse(roots, totalRange, record)
}
}