Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

201 lines (177 sloc) 7.359 kb
package scala.tools.nsc
package interactive
import scala.reflect.internal.util.{SourceFile, BatchSourceFile, RangePosition}
import scala.collection.mutable.ArrayBuffer
import scala.reflect.internal.Chars.{isLineBreakChar, isWhitespace}
import ast.parser.Tokens._
@deprecated("SI-6458: Instrumentation logic will be moved out of the compiler.","2.10.0")
trait ScratchPadMaker { self: Global =>
import definitions._
private case class Patch(offset: Int, text: String)
private class Patcher(contents: Array[Char], lex: LexicalStructure, endOffset: Int) extends Traverser {
var objectName: String = ""
private val patches = new ArrayBuffer[Patch]
private val toPrint = new ArrayBuffer[String]
private var skipped = 0
private var resNum: Int = -1
private def nextRes(): String = {
resNum += 1
"res$"+resNum
}
private def nameType(name: String, tpe: Type): String = {
// if name ends in symbol character, add a space to separate it from the following ':'
val pad = if (Character.isLetter(name.last) || Character.isDigit(name.last)) "" else " "
name+pad+": "+tpe
}
private def nameType(sym: Symbol): String = nameType(sym.name.decoded, sym.tpe)
private def literal(str: String) = "\"\"\""+str+"\"\"\""
private val prologue = ";import scala.runtime.WorksheetSupport._; def main(args: Array[String])=$execute{"
private val epilogue = "}"
private def applyPendingPatches(offset: Int) = {
if (skipped == 0) patches += Patch(offset, prologue)
for (msg <- toPrint) patches += Patch(offset, ";System.out.println("+msg+")")
toPrint.clear()
}
/** The position where to insert an instrumentation statement in front of giuven statement.
* This is at the latest `stat.pos.start`. But in order not to mess with column numbers
* in position we try to insert it at the end of the previous token instead.
* Furthermore, `(' tokens have to be skipped because they do not show up
* in statement range positions.
*/
private def instrumentPos(start: Int): Int = {
val (prevToken, prevStart, prevEnd) = lex.locate(start - 1)
if (prevStart >= start) start
else if (prevToken == LPAREN) instrumentPos(prevStart)
else prevEnd
}
private def addSkip(stat: Tree): Unit = {
val ipos = instrumentPos(stat.pos.start)
if (stat.pos.start > skipped) applyPendingPatches(ipos)
if (stat.pos.start >= endOffset)
patches += Patch(ipos, ";$stop()")
var end = stat.pos.end
if (end > skipped) {
while (end < contents.length && !isLineBreakChar(contents(end))) end += 1
patches += Patch(ipos, ";$skip("+(end-skipped)+"); ")
skipped = end
}
}
private def addSandbox(expr: Tree) = {}
// patches += (Patch(expr.pos.start, "sandbox("), Patch(expr.pos.end, ")"))
private def resultString(prefix: String, expr: String) =
literal(prefix + " = ") + " + $show(" + expr + ")"
private def traverseStat(stat: Tree) =
if (stat.pos.isInstanceOf[RangePosition]) {
stat match {
case ValDef(_, _, _, rhs) =>
addSkip(stat)
if (stat.symbol.isLazy)
toPrint += literal(nameType(stat.symbol) + " = <lazy>")
else if (!stat.symbol.isSynthetic) {
addSandbox(rhs)
toPrint += resultString(nameType(stat.symbol), stat.symbol.name.toString)
}
case DefDef(_, _, _, _, _, _) =>
addSkip(stat)
toPrint += literal(nameType(stat.symbol))
case Annotated(_, arg) =>
traverse(arg)
case DocDef(_, defn) =>
traverse(defn)
case _ =>
if (stat.isTerm) {
addSkip(stat)
if (stat.tpe.typeSymbol == UnitClass) {
addSandbox(stat)
} else {
val resName = nextRes()
val dispResName = resName filter ('$' != _)
val offset = instrumentPos(stat.pos.start)
patches += Patch(offset, "val " + resName + " = ")
addSandbox(stat)
toPrint += resultString(nameType(dispResName, stat.tpe), resName)
}
}
}
}
override def traverse(tree: Tree): Unit = tree match {
case PackageDef(_, _) =>
super.traverse(tree)
case ModuleDef(_, name, Template(_, _, body)) =>
val topLevel = objectName.isEmpty
if (topLevel) {
objectName = tree.symbol.fullName
body foreach traverseStat
if (skipped != 0) { // don't issue prologue and epilogue if there are no instrumented statements
applyPendingPatches(skipped)
patches += Patch(skipped, epilogue)
}
}
case _ =>
}
/** The patched text.
* @require traverse is run first
*/
def result: Array[Char] = {
val reslen = contents.length + (patches map (_.text.length)).sum
val res = Array.ofDim[Char](reslen)
var lastOffset = 0
var from = 0
var to = 0
for (Patch(offset, text) <- patches) {
val delta = offset - lastOffset
assert(delta >= 0)
Array.copy(contents, from, res, to, delta)
from += delta
to += delta
lastOffset = offset
text.copyToArray(res, to)
to += text.length
}
assert(contents.length - from == reslen - to)
Array.copy(contents, from, res, to, contents.length - from)
res
}
}
class LexicalStructure(source: SourceFile) {
val token = new ArrayBuffer[Int]
val startOffset = new ArrayBuffer[Int]
val endOffset = new ArrayBuffer[Int]
private val scanner = new syntaxAnalyzer.UnitScanner(new CompilationUnit(source))
scanner.init()
while (scanner.token != EOF) {
startOffset += scanner.offset
token += scanner.token
scanner.nextToken()
endOffset += scanner.lastOffset
}
/** @return token that starts before or at offset, its startOffset, its endOffset
*/
def locate(offset: Int): (Int, Int, Int) = {
var lo = 0
var hi = token.length - 1
while (lo < hi) {
val mid = (lo + hi + 1) / 2
if (startOffset(mid) <= offset) lo = mid
else hi = mid - 1
}
(token(lo), startOffset(lo), endOffset(lo))
}
}
/** Compute an instrumented version of a sourcefile.
* @param source The given sourcefile.
* @param line The line up to which results should be printed, -1 = whole document.
* @return A pair consisting of
* - the fully qualified name of the first top-level object definition in the file.
* or "" if there are no object definitions.
* - the text of the instrumented program which, when run,
* prints its output and all defined values in a comment column.
*/
protected def instrument(source: SourceFile, line: Int): (String, Array[Char]) = {
val tree = typedTree(source, true)
val endOffset = if (line < 0) source.length else source.lineToOffset(line + 1)
val patcher = new Patcher(source.content, new LexicalStructure(source), endOffset)
patcher.traverse(tree)
(patcher.objectName, patcher.result)
}
}
Jump to Line
Something went wrong with that request. Please try again.