summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Suereth <Joshua.Suereth@gmail.com>2012-09-04 12:16:44 -0700
committerJosh Suereth <Joshua.Suereth@gmail.com>2012-09-04 12:16:44 -0700
commitd34e8abaacf7a51e05630557681e8267d7050e3a (patch)
treea96e186e8ee3c6be67451269f1b94ab81b6d8acc
parent3041fc5fe3b36e618cd0a4b2d909c219050a5c3f (diff)
parentbcf1d9a5f5c1d0319b51cd3dcce9ecebdeb12feb (diff)
downloadscala-d34e8abaacf7a51e05630557681e8267d7050e3a.tar.gz
scala-d34e8abaacf7a51e05630557681e8267d7050e3a.tar.bz2
scala-d34e8abaacf7a51e05630557681e8267d7050e3a.zip
Merge pull request #1219 from odersky/topic/worksheet-instrumenter
More worksheet nstrumentation changes
-rw-r--r--src/compiler/scala/tools/nsc/interactive/REPL.scala46
-rw-r--r--src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala61
-rw-r--r--src/compiler/scala/tools/nsc/scratchpad/CommentOutputStream.scala18
-rw-r--r--src/compiler/scala/tools/nsc/scratchpad/CommentWriter.scala42
-rw-r--r--src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala89
-rw-r--r--src/library/scala/runtime/WorksheetSupport.scala6
6 files changed, 51 insertions, 211 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala
index 2d93c77ca4..afac5828e5 100644
--- a/src/compiler/scala/tools/nsc/interactive/REPL.scala
+++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala
@@ -133,50 +133,6 @@ object REPL {
iSourceName
}
- /** Compile instrumented source file
- * @param iSourceName The name of the instrumented source file
- * @param arguments Further argumenrs to pass to the compiler
- * @return Optionallu, if no -d option is given, the virtual directory
- * contained the generated bytecode classes
- def compileInstrumented(iSourceName: String, arguments: List[String]): Option[AbstractFile] = {
- println("compiling "+iSourceName)
- val command = new CompilerCommand(iSourceName :: arguments, reporter.error(scala.reflect.internal.util.NoPosition, _))
- val virtualDirectoryOpt =
- if (arguments contains "-d")
- None
- else {
- val vdir = new VirtualDirectory("(memory)", None)
- command.settings.outputDirs setSingleOutput vdir
- Some(vdir)
- }
- val compiler = new scala.tools.nsc.Global(command.settings, reporter)
- val run = new compiler.Run()
- println("compiling: "+command.files)
- run compile command.files
- virtualDirectoryOpt
- }
-
- /** Run instrumented bytecode file
- * @param vdir Optionally, the virtual directory containing the generated bytecode classes
- * @param iFullName The full name of the generated object
- * @param stripped The contents original source file without any right hand column comments.
- * @return The generated file content containing original source in the left column
- * and outputs in the right column
- */
- def runInstrumented(vdirOpt: Option[AbstractFile], iFullName: String, stripped: Array[Char]): Array[Char] = {
- val defaultClassLoader = getClass.getClassLoader
- val classLoader = vdirOpt match {
- case Some(vdir) => new AbstractFileClassLoader(vdir, defaultClassLoader)
- case None => defaultClassLoader
- }
- println("running "+iFullName)
- val si = new SourceInserter(stripped)
- Executor.execute(iFullName, si, classLoader)
- println("done")
- si.currentContents
- }
- */
-
/** The method for implementing worksheet functionality.
* @param arguments a file name, followed by optional command line arguments that are passed
* to the compiler that processes the instrumented source.
@@ -191,7 +147,7 @@ object REPL {
// strip right hand side comment column and any trailing spaces from all lines
val strippedContents = SourceInserter.stripRight(source.content)
val strippedSource = new BatchSourceFile(source.file, strippedContents)
- println("stripped source = "+strippedSource)
+ println("stripped source = "+strippedSource+":"+strippedContents.mkString)
comp.askReload(List(strippedSource), reloadResult)
comp.askInstrumented(strippedSource, line, instrumentedResult)
using(instrumentedResult) {
diff --git a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala
index 2aed99657e..0080cfd753 100644
--- a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala
+++ b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala
@@ -4,6 +4,7 @@ package interactive
import scala.reflect.internal.util.{SourceFile, BatchSourceFile, RangePosition}
import collection.mutable.ArrayBuffer
import reflect.internal.Chars.{isLineBreakChar, isWhitespace}
+import ast.parser.Tokens._
trait ScratchPadMaker { self: Global =>
@@ -11,7 +12,7 @@ trait ScratchPadMaker { self: Global =>
private case class Patch(offset: Int, text: String)
- private class Patcher(contents: Array[Char], endOffset: Int) extends Traverser {
+ private class Patcher(contents: Array[Char], lex: LexicalStructure, endOffset: Int) extends Traverser {
var objectName: String = ""
private val patches = new ArrayBuffer[Patch]
@@ -24,9 +25,13 @@ trait ScratchPadMaker { self: Global =>
"res$"+resNum
}
- private def nameType(name: String, tpe: Type): String = name+": "+tpe
+ 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.toString, sym.tpe)
+ private def nameType(sym: Symbol): String = nameType(sym.name.decoded, sym.tpe)
private def literal(str: String) = "\"\"\""+str+"\"\"\""
@@ -42,19 +47,19 @@ trait ScratchPadMaker { self: Global =>
/** 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 preceding line instead.
- * To be safe, this can be done only if there's only whitespace between that position and
- * statement's start position.
+ * 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(stat: Tree): Int = {
- var start = stat.pos.start
- while (start > 0 && isWhitespace(contents(start - 1))) start -= 1
- if (start > 0 && isLineBreakChar(contents(start - 1))) start -= 1
- start
+ 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)
+ val ipos = instrumentPos(stat.pos.start)
if (stat.pos.start > skipped) applyPendingPatches(ipos)
if (stat.pos.start >= endOffset)
patches += Patch(ipos, ";$stop()")
@@ -98,7 +103,8 @@ trait ScratchPadMaker { self: Global =>
} else {
val resName = nextRes()
val dispResName = resName filter ('$' != _)
- patches += Patch(stat.pos.start, "val " + resName + " = ")
+ val offset = instrumentPos(stat.pos.start)
+ patches += Patch(offset, "val " + resName + " = ")
addSandbox(stat)
toPrint += resultString(nameType(dispResName, stat.tpe), resName)
}
@@ -146,6 +152,33 @@ trait ScratchPadMaker { self: Global =>
}
}
+ 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.
@@ -158,7 +191,7 @@ trait ScratchPadMaker { self: Global =>
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, endOffset)
+ val patcher = new Patcher(source.content, new LexicalStructure(source), endOffset)
patcher.traverse(tree)
(patcher.objectName, patcher.result)
}
diff --git a/src/compiler/scala/tools/nsc/scratchpad/CommentOutputStream.scala b/src/compiler/scala/tools/nsc/scratchpad/CommentOutputStream.scala
deleted file mode 100644
index 92ccd79df9..0000000000
--- a/src/compiler/scala/tools/nsc/scratchpad/CommentOutputStream.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-package scala.tools.nsc.scratchpad
-
-import java.io.OutputStream
-
-class CommentOutputStream(out: CommentWriter, encoding: String = "") extends OutputStream {
-
- override def write(bs: Array[Byte]) =
- out.write(if (encoding.isEmpty) new String(bs) else new String(bs, encoding))
-
- override def write(bs: Array[Byte], off: Int, len: Int) =
- out.write(if (encoding.isEmpty) new String(bs, off, len) else new String(bs, off, len, encoding))
-
- override def write(ch: Int) =
- write(Array(ch.toByte))
-
- override def close() = out.close()
- override def flush() = out.flush()
-} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/scratchpad/CommentWriter.scala b/src/compiler/scala/tools/nsc/scratchpad/CommentWriter.scala
deleted file mode 100644
index eb8880e437..0000000000
--- a/src/compiler/scala/tools/nsc/scratchpad/CommentWriter.scala
+++ /dev/null
@@ -1,42 +0,0 @@
-package scala.tools.nsc.scratchpad
-
-import java.io.Writer
-import reflect.internal.Chars._
-
-
-class CommentWriter(underlying: SourceInserter, startCol: Int = 40, endCol: Int = 152) extends Writer {
-
- private def rightCol(marker: String) = {
- while (underlying.column < startCol) underlying.write(' ')
- underlying.write(marker)
- }
-
- private var lastWasNL = false
-
- private def writeChar(ch: Char) = {
- if (underlying.column >= endCol) {
- underlying.write('\n'); rightCol("//| ")
- }
- if (underlying.column < startCol) rightCol("//> ")
- underlying.write(ch)
- lastWasNL = isLineBreakChar(ch)
- }
-
- override def write(chs: Array[Char], off: Int, len: Int) = {
- for (i <- off until off + len) writeChar(chs(i))
- flush()
- }
-
- def skip(len: Int) {
- if (lastWasNL) {
- underlying.backspace()
- lastWasNL = false
- }
- underlying.skip(len)
- if (underlying.column >= startCol) underlying.write('\n')
- }
-
- override def close() = underlying.close()
- override def flush() = underlying.flush()
-}
-
diff --git a/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala b/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala
index 42a35dc642..1c4fad5511 100644
--- a/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala
+++ b/src/compiler/scala/tools/nsc/scratchpad/SourceInserter.scala
@@ -21,92 +21,3 @@ object SourceInserter {
(prefixes mkString "\n").toArray
}
}
-class SourceInserter(contents: Array[Char], start: Int = 0, tabInc: Int = 8) extends Writer {
-
- private var buf = contents
- private var offset = start
- private var hilen = contents.length
-
- def length = offset + hilen
-
- private def currentColumn: Int = {
- var i = offset
- while (i > 0 && !isLineBreakChar(buf(i - 1))) i -= 1
- var col = 0
- while (i < offset) {
- col = if (buf(i) == '\t') (col + tabInc) / tabInc * tabInc else col + 1
- i += 1
- }
- col
- }
-
- private var col = currentColumn
-
- def column = synchronized { col }
-
- private def addCapacity(n: Int) = {
- val newlength = length + n
- while (newlength > buf.length) {
- val buf1 = Array.ofDim[Char](buf.length * 2)
- Array.copy(buf, 0, buf1, 0, offset)
- Array.copy(buf, buf.length - hilen, buf1, buf1.length - hilen, hilen)
- buf = buf1
- }
- }
-
- private def insertChar(ch: Char) = {
-// Console.err.print("["+ch+"]")
- buf(offset) = ch
- offset += 1
- ch match {
- case LF => col = 0
- case '\t' => col = (col + tabInc) / tabInc * tabInc
- case _ => col += 1
- }
- }
-
- override def write(ch: Int) = synchronized {
- addCapacity(1)
- insertChar(ch.toChar)
- }
-
- override def write(chs: Array[Char], off: Int, len: Int) = synchronized {
- addCapacity(len)
- for (i <- off until off + len) insertChar(chs(i))
- }
-
- override def close() {
- }
-
- override def flush() {
- // signal buffer change
- }
-
- def currentContents = synchronized {
- if (length == buf.length) buf
- else {
- val res = Array.ofDim[Char](length)
- Array.copy(buf, 0, res, 0, offset)
- Array.copy(buf, buf.length - hilen, res, offset, hilen)
- res
- }
- }
-
- def backspace() = synchronized {
- offset -= 1
- if (offset > 0 && buf(offset) == LF && buf(offset - 1) == CR) offset -=1
- }
-
- def currentChar = synchronized {
- buf(buf.length - hilen)
- }
-
- def skip(len: Int) = synchronized {
- for (i <- 0 until len) {
- val ch = currentChar
- hilen -= 1
- insertChar(ch)
- }
- }
-}
-
diff --git a/src/library/scala/runtime/WorksheetSupport.scala b/src/library/scala/runtime/WorksheetSupport.scala
index 6f2a4d382d..a003bba034 100644
--- a/src/library/scala/runtime/WorksheetSupport.scala
+++ b/src/library/scala/runtime/WorksheetSupport.scala
@@ -40,9 +40,9 @@ object WorksheetSupport {
write((currentOffset+" ").getBytes)
}
out.write(c)
- col =
+ col =
if (c == '\n') -1
- else if (c == '\t') (col / tabInc) * tabInc + tabInc
+ else if (c == '\t') (col / tabInc) * tabInc + tabInc
else col + 1
if (col >= width) writeOne('\n')
}
@@ -86,7 +86,7 @@ object WorksheetSupport {
def $stop() = throw new StopException
- def $show(x: Any): String = stringOf(x, scala.Int.MaxValue)
+ def $show(x: Any): String = stringOf(x)
}
class StopException extends Exception