From a38f03ba966907c2996944bf1e82f51760c661d9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 10 Nov 2011 18:15:35 +0000 Subject: More refinements for the scratchpad. --- .../scala/tools/nsc/interactive/REPL.scala | 119 ++++++++++++++++----- .../scala/tools/nsc/scratchpad/Executor.scala | 4 +- 2 files changed, 93 insertions(+), 30 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala index ea30ce2516..81d4faa36e 100644 --- a/src/compiler/scala/tools/nsc/interactive/REPL.scala +++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala @@ -12,6 +12,7 @@ import scala.tools.nsc.ast._ import scala.tools.nsc.reporters._ import scala.tools.nsc.io._ import scala.tools.nsc.scratchpad.{Executor, SourceInserter} +import scala.tools.nsc.interpreter.AbstractFileClassLoader import java.io.{File, FileWriter} /** Interface of interactive compiler to a client such as an IDE @@ -118,34 +119,84 @@ object REPL { show(structureResult) } - /** This is the method for implement worksheet functionality + /** Write instrumented source file to disk. + * @param iFullName The full name of the first top-level object in source + * @param iContents An Array[Char] containing the instrumented source + * @return The name of the instrumented source file */ - def instrument(source: SourceFile, line: Int): Option[Array[Char]] = { + def writeInstrumented(iFullName: String, iContents: Array[Char]): String = { + val iSimpleName = iFullName drop ((iFullName lastIndexOf '.') + 1) + val iSourceName = iSimpleName + "$instrumented.scala" + val ifile = new FileWriter(iSourceName) + ifile.write(iContents) + ifile.close() + 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.tools.nsc.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. + * @param line A line number that controls uop to which line results should be produced + * If line = -1, results are produced for all expressions in the worksheet. + * @return The generated file content containing original source in the left column + * and outputs in the right column, or None if the presentation compiler + * does not respond to askInstrumented. + */ + def instrument(arguments: List[String], line: Int): Option[Array[Char]] = { + val source = toSourceFile(arguments.head) // strip right hand side comment column and any trailing spaces from all lines val strippedSource = new BatchSourceFile(source.file, SourceInserter.stripRight(source.content)) comp.askReload(List(strippedSource), reloadResult) - // todo: Display strippedSource in place of source comp.askInstrumented(strippedSource, line, instrumentedResult) - using(instrumentedResult) { case (iFullName, iContents) => - // iFullName: The full name of the first top-level object in source - // iContents: An Array[Char] containing the instrumented source - // Create a file from iContents so that it can be compiled - // The name here is +"$instrumented.scala", but - // it could be anything. - val iSimpleName = iFullName drop ((iFullName lastIndexOf '.') + 1) - val iSourceName = iSimpleName + "$instrumented.scala" - val ifile = new FileWriter(iSourceName) - ifile.write(iContents) - ifile.close() - println("compiling "+iSourceName) - // Compile instrumented source - scala.tools.nsc.Main.process(Array(iSourceName, "-verbose", "-d", "/classes")) - // Run instrumented source, inserting all output into stripped source - println("running "+iFullName) - val si = new SourceInserter(strippedSource.content) - Executor.execute(iFullName, si) - println("done") - si.currentContents + using(instrumentedResult) { + case (iFullName, iContents) => + val iSourceName = writeInstrumented(iFullName, iContents) + val vdirOpt = compileInstrumented(iSourceName, arguments.tail) + runInstrumented(vdirOpt, iFullName, strippedSource.content) } } @@ -170,17 +221,29 @@ object REPL { doComplete(makePos(file, off1, off2)) case List("complete", file, off1) => doComplete(makePos(file, off1, off1)) - case List("instrument", file) => - println(instrument(toSourceFile(file), -1).map(_.mkString)) - case List("instrument", file, line) => - println(instrument(toSourceFile(file), line.toInt).map(_.mkString)) + case "instrument" :: arguments => + println(instrument(arguments, -1).map(_.mkString)) + case "instrumentTo" :: line :: arguments => + println(instrument(arguments, line.toInt).map(_.mkString)) case List("quit") => comp.askShutdown() exit(1) // Don't use sys yet as this has to run on 2.8.2 also. case List("structure", file) => doStructure(file) case _ => - println("unrecongized command") + print("""Available commands: + | reload ... + | reloadAndAskType + | typed + | typeat + | typeat + | complete + | compile + | instrument * + | instrumentTo * + | structure + | quit + |""".stripMargin) } } } diff --git a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala b/src/compiler/scala/tools/nsc/scratchpad/Executor.scala index 673d5827f6..ff0f94d897 100644 --- a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala +++ b/src/compiler/scala/tools/nsc/scratchpad/Executor.scala @@ -15,7 +15,7 @@ object Executor { /** Execute module with given name, redirecting all output to given * source inserter. Catch all exceptions and print stacktrace of underlying causes. */ - def execute(name: String, si: SourceInserter) { + def execute(name: String, si: SourceInserter, classLoader: ClassLoader = getClass.getClassLoader) { val oldSysOut = System.out val oldSysErr = System.err val oldConsOut = Console.out @@ -28,7 +28,7 @@ object Executor { Console.setOut(newOut) Console.setErr(newOut) try { - singletonInstance(name) + singletonInstance(name, classLoader) } catch { case ex: Throwable => unwrapThrowable(ex) match { -- cgit v1.2.3