aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2016-06-03 10:53:04 +0200
committerDmitry Petrashko <dark@d-d.me>2016-06-03 10:53:04 +0200
commit030ff82070197f0c126f5c0287e076b0f6b6dd8d (patch)
tree4d06d4952ccf55a49d5d40c88dbef5ce89cd24b8
parent845b98186047f38013e2f6aa35508e974eedafb7 (diff)
parent5c29e0ab8ba6220d2e4933579a6fdf9b84f8fc1e (diff)
downloaddotty-030ff82070197f0c126f5c0287e076b0f6b6dd8d.tar.gz
dotty-030ff82070197f0c126f5c0287e076b0f6b6dd8d.tar.bz2
dotty-030ff82070197f0c126f5c0287e076b0f6b6dd8d.zip
Merge pull request #1299 from felixmulder/topic/prepare-repl-for-bridge
Prepare REPL for dotty-bridge
-rw-r--r--src/dotty/tools/dotc/repl/CompilingInterpreter.scala62
-rw-r--r--src/dotty/tools/dotc/repl/Interpreter.scala11
-rw-r--r--src/dotty/tools/dotc/repl/InterpreterLoop.scala25
-rw-r--r--src/dotty/tools/dotc/repl/REPL.scala28
4 files changed, 110 insertions, 16 deletions
diff --git a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala
index 7b8ba698a..b6a3e388e 100644
--- a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala
+++ b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala
@@ -228,6 +228,65 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit
}
}
+ private def loadAndSetValue(objectName: String, value: AnyRef) = {
+ /** This terrible string is the wrapped class's full name inside the
+ * classloader:
+ * lineX$object$$iw$$iw$list$object
+ */
+ val objName: String = List(
+ currentLineName + INTERPRETER_WRAPPER_SUFFIX,
+ INTERPRETER_IMPORT_WRAPPER,
+ INTERPRETER_IMPORT_WRAPPER,
+ objectName
+ ).mkString("$")
+
+ try {
+ val resObj: Class[_] = Class.forName(objName, true, classLoader)
+ val setMethod = resObj.getDeclaredMethods.find(_.getName == "set")
+
+ setMethod.fold(false) { method =>
+ method.invoke(resObj, value) == null
+ }
+ } catch {
+ case NonFatal(_) =>
+ // Unable to set value on object due to exception during reflection
+ false
+ }
+ }
+
+ /** This bind is implemented by creating an object with a set method and a
+ * field `value`. The value is then set via Java reflection.
+ *
+ * Example: We want to bind a value `List(1,2,3)` to identifier `list` from
+ * sbt. The bind method accomplishes this by creating the following:
+ * {{{
+ * object ContainerObjectWithUniqueID {
+ * var value: List[Int] = _
+ * def set(x: Any) = value = x.asInstanceOf[List[Int]]
+ * }
+ * val list = ContainerObjectWithUniqueID.value
+ * }}}
+ *
+ * Between the object being created and the value being assigned, the value
+ * inside the object is set via reflection.
+ */
+ override def bind(id: String, boundType: String, value: AnyRef)(implicit ctx: Context): Interpreter.Result =
+ interpret(
+ """
+ |object %s {
+ | var value: %s = _
+ | def set(x: Any) = value = x.asInstanceOf[%s]
+ |}
+ """.stripMargin.format(id + INTERPRETER_WRAPPER_SUFFIX, boundType, boundType)
+ ) match {
+ case Interpreter.Success if loadAndSetValue(id + INTERPRETER_WRAPPER_SUFFIX, value) =>
+ val line = "val %s = %s.value".format(id, id + INTERPRETER_WRAPPER_SUFFIX)
+ interpret(line)
+ case Interpreter.Error | Interpreter.Incomplete =>
+ out.println("Set failed in bind(%s, %s, %s)".format(id, boundType, value))
+ Interpreter.Error
+ }
+
/** Trait collecting info about one of the statements of an interpreter request */
private trait StatementInfo {
/** The statement */
@@ -738,6 +797,9 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit
INTERPRETER_LINE_PREFIX + num
}
+ private def currentLineName =
+ INTERPRETER_LINE_PREFIX + (nextLineNo - 1)
+
/** next result variable number to use */
private var nextVarNameNo = 0
diff --git a/src/dotty/tools/dotc/repl/Interpreter.scala b/src/dotty/tools/dotc/repl/Interpreter.scala
index 6a292dfe2..e11fbf5cc 100644
--- a/src/dotty/tools/dotc/repl/Interpreter.scala
+++ b/src/dotty/tools/dotc/repl/Interpreter.scala
@@ -25,12 +25,15 @@ object Interpreter {
trait Interpreter {
import Interpreter._
- /** Interpret one line of input. All feedback, including parse errors
- * and evaluation results, are printed via the context's reporter.
- * reporter. Values defined are available for future interpreted strings.
- */
+ /** Interpret one line of input. All feedback, including parse errors and
+ * evaluation results, are printed via the context's reporter. Values
+ * defined are available for future interpreted strings.
+ */
def interpret(line: String)(implicit ctx: Context): Result
+ /** Tries to bind an id to a value, returns the outcome of trying to bind */
+ def bind(id: String, boundType: String, value: AnyRef)(implicit ctx: Context): Result
+
/** Suppress output during evaluation of `operation`. */
def beQuietDuring[T](operation: => T): T
diff --git a/src/dotty/tools/dotc/repl/InterpreterLoop.scala b/src/dotty/tools/dotc/repl/InterpreterLoop.scala
index 14a50fdf1..7e5dcc7f1 100644
--- a/src/dotty/tools/dotc/repl/InterpreterLoop.scala
+++ b/src/dotty/tools/dotc/repl/InterpreterLoop.scala
@@ -68,17 +68,6 @@ class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Con
val version = ".next (pre-alpha)"
- /** The first interpreted command always takes a couple of seconds
- * due to classloading. To bridge the gap, we warm up the interpreter
- * by letting it interpret a dummy line while waiting for the first
- * line of input to be entered.
- */
- def firstLine(): String = {
- interpreter.beQuietDuring(
- interpreter.interpret("val theAnswerToLifeInTheUniverseAndEverything = 21 * 2"))
- in.readLine(prompt)
- }
-
/** The main read-eval-print loop for the interpreter. It calls
* `command()` for each line of input.
*/
@@ -177,6 +166,15 @@ class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Con
(true, shouldReplay)
}
+ def silentlyRun(cmds: List[String]): Unit = cmds.foreach { cmd =>
+ interpreter.beQuietDuring(interpreter.interpret(cmd))
+ }
+
+ def silentlyBind(values: Array[(String, Any)]): Unit = values.foreach { case (id, value) =>
+ interpreter.beQuietDuring(
+ interpreter.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value.asInstanceOf[AnyRef]))
+ }
+
/** Interpret expressions starting with the first line.
* Read lines until a complete compilation unit is available
* or until a syntax error has been seen. If a full unit is
@@ -207,7 +205,10 @@ class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Con
try {
if (!ctx.reporter.hasErrors) { // if there are already errors, no sense to continue
printWelcome()
- repl(firstLine())
+ silentlyRun(config.initialCommands)
+ silentlyBind(config.boundValues)
+ repl(in.readLine(prompt))
+ silentlyRun(config.cleanupCommands)
}
} finally {
closeInterpreter()
diff --git a/src/dotty/tools/dotc/repl/REPL.scala b/src/dotty/tools/dotc/repl/REPL.scala
index 977f67719..1fcb055d6 100644
--- a/src/dotty/tools/dotc/repl/REPL.scala
+++ b/src/dotty/tools/dotc/repl/REPL.scala
@@ -52,6 +52,34 @@ object REPL {
def context(ctx: Context): Context = ctx
+ /** The first interpreted commands always take a couple of seconds due to
+ * classloading. To bridge the gap, we warm up the interpreter by letting
+ * it interpret at least a dummy line while waiting for the first line of
+ * input to be entered.
+ */
+ val initialCommands: List[String] =
+ "val theAnswerToLifeInTheUniverseAndEverything = 21 * 2" :: Nil
+
+ /** Before exiting, the interpreter will also run the cleanup commands
+ * issued in the variable below. This is useful if your REPL creates
+ * things during its run that should be dealt with before shutdown.
+ */
+ val cleanupCommands: List[String] = Nil
+
+ /** Initial values in the REPL can also be bound from runtime. Override
+ * this variable in the following manner to bind a variable at the start
+ * of the REPL session:
+ *
+ * {{{
+ * override val boundValues = Array("exampleList" -> List(1, 1, 2, 3, 5))
+ * }}}
+ *
+ * This is useful if you've integrated the REPL as part of your project
+ * and already have objects available during runtime that you'd like to
+ * inspect.
+ */
+ val boundValues: Array[(String, Any)] = Array.empty[(String, Any)]
+
/** The default input reader */
def input(in: Interpreter)(implicit ctx: Context): InteractiveReader = {
val emacsShell = System.getProperty("env.emacs", "") != ""