summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala64
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Imports.scala12
-rw-r--r--test/files/neg/t5148.check8
-rw-r--r--test/files/run/t7747-repl.check286
-rw-r--r--test/files/run/t7747-repl.scala69
6 files changed, 417 insertions, 23 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index b8ca4adc14..7568c789fb 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -174,6 +174,7 @@ trait ScalaSettings extends AbsScalaSettings
val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
val Ymacronoexpand = BooleanSetting ("-Ymacro-no-expand", "Don't expand macros. Might be useful for scaladoc and presentation compiler, but will crash anything which uses macros and gets past typer.")
val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
+ val Yreplclassbased = BooleanSetting ("-Yrepl-class-based", "Use classes to wrap REPL snippets instead of objects")
val Yreploutdir = StringSetting ("-Yrepl-outdir", "path", "Write repl-generated classfiles to given output directory (use \"\" to generate a temporary dir)" , "")
val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.")
val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.").withDeprecationMessage(removalIn212)
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index e4a3416152..0d55423247 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -272,6 +272,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
case s => s
} mkString "."
)
+ def readRootPath(readPath: String) = getModuleIfDefined(readPath)
abstract class PhaseDependentOps {
def shift[T](op: => T): T
@@ -700,7 +701,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
*
* Read! Eval! Print! Some of that not yet centralized here.
*/
- class ReadEvalPrint(lineId: Int) {
+ class ReadEvalPrint(val lineId: Int) {
def this() = this(freshLineId())
val packageName = sessionNames.line + lineId
@@ -777,7 +778,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
* following accessPath into the outer one.
*/
def resolvePathToSymbol(accessPath: String): Symbol = {
- val readRoot = getModuleIfDefined(readPath) // the outermost wrapper
+ val readRoot = readRootPath(readPath) // the outermost wrapper
(accessPath split '.').foldLeft(readRoot: Symbol) {
case (sym, "") => sym
case (sym, name) => exitingTyper(termMember(sym, name))
@@ -848,30 +849,69 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
/** Code to import bound names from previous lines - accessPath is code to
* append to objectName to access anything bound by request.
*/
- val ComputedImports(importsPreamble, importsTrailer, accessPath) =
- exitingTyper(importsCode(referencedNames.toSet))
+ lazy val ComputedImports(importsPreamble, importsTrailer, accessPath) =
+ exitingTyper(importsCode(referencedNames.toSet, ObjectSourceCode))
/** the line of code to compute */
def toCompute = line
- def fullPath(vname: String) = s"${lineRep.readPath}$accessPath.`$vname`"
+ /** The path of the value that contains the user code. */
+ def fullAccessPath = s"${lineRep.readPath}$accessPath"
+
+ /** The path of the given member of the wrapping instance. */
+ def fullPath(vname: String) = s"$fullAccessPath.`$vname`"
/** generate the source code for the object that computes this request */
- private object ObjectSourceCode extends IMain.CodeAssembler[MemberHandler] {
+ abstract class Wrapper extends IMain.CodeAssembler[MemberHandler] {
def path = originalPath("$intp")
def envLines = {
if (!isReplPower) Nil // power mode only for now
else List("def %s = %s".format("$line", tquoted(originalLine)), "def %s = Nil".format("$trees"))
}
-
- val preamble = """
- |object %s {
+ def preamble = s"""
+ |$preambleHeader
|%s%s%s
- """.stripMargin.format(lineRep.readName, envLines.map(" " + _ + ";\n").mkString, importsPreamble, indentCode(toCompute))
- val postamble = importsTrailer + "\n}"
+ """.stripMargin.format(lineRep.readName, envLines.map(" " + _ + ";\n").mkString,
+ importsPreamble, indentCode(toCompute))
+
val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this
+
+ /** A format string with %s for $read, specifying the wrapper definition. */
+ def preambleHeader: String
+
+ /** Like preambleHeader for an import wrapper. */
+ def prewrap: String = preambleHeader + "\n"
+
+ /** Like postamble for an import wrapper. */
+ def postwrap: String
+ }
+
+ private class ObjectBasedWrapper extends Wrapper {
+ def preambleHeader = "object %s {"
+
+ def postamble = importsTrailer + "\n}"
+
+ def postwrap = "}\n"
}
+ private class ClassBasedWrapper extends Wrapper {
+ def preambleHeader = "class %s extends Serializable {"
+
+ /** Adds an object that instantiates the outer wrapping class. */
+ def postamble = s"""$importsTrailer
+ |}
+ |object ${lineRep.readName} extends ${lineRep.readName}
+ |""".stripMargin
+
+ import nme.{ INTERPRETER_IMPORT_WRAPPER => iw }
+
+ /** Adds a val that instantiates the wrapping class. */
+ def postwrap = s"}\nval $iw = new $iw\n"
+ }
+
+ private lazy val ObjectSourceCode: Wrapper =
+ if (settings.Yreplclassbased) new ClassBasedWrapper else new ObjectBasedWrapper
+
private object ResultObjectSourceCode extends IMain.CodeAssembler[MemberHandler] {
/** We only want to generate this code when the result
* is a value which can be referred to as-is.
@@ -890,7 +930,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
| (""
""".stripMargin.format(
lineRep.evalName, evalResult, lineRep.printName,
- executionWrapper, lineRep.readName + accessPath
+ executionWrapper, fullAccessPath
)
val postamble = """
diff --git a/src/repl/scala/tools/nsc/interpreter/Imports.scala b/src/repl/scala/tools/nsc/interpreter/Imports.scala
index ff7bfd432c..5244858a62 100644
--- a/src/repl/scala/tools/nsc/interpreter/Imports.scala
+++ b/src/repl/scala/tools/nsc/interpreter/Imports.scala
@@ -92,7 +92,7 @@ trait Imports {
* last one imported is actually usable.
*/
case class ComputedImports(prepend: String, append: String, access: String)
- protected def importsCode(wanted: Set[Name]): ComputedImports = {
+ protected def importsCode(wanted: Set[Name], wrapper: Request#Wrapper): ComputedImports = {
/** Narrow down the list of requests from which imports
* should be taken. Removes requests which cannot contribute
* useful imports for the specified set of wanted names.
@@ -130,13 +130,15 @@ trait Imports {
// add code for a new object to hold some imports
def addWrapper() {
- val impname = nme.INTERPRETER_IMPORT_WRAPPER
- code append "object %s {\n".format(impname)
- trailingBraces append "}\n"
- accessPath append ("." + impname)
+ import nme.{ INTERPRETER_IMPORT_WRAPPER => iw }
+ code append (wrapper.prewrap format iw)
+ trailingBraces append wrapper.postwrap
+ accessPath append s".$iw"
currentImps.clear()
}
+
def maybeWrap(names: Name*) = if (names exists currentImps) addWrapper()
+
def wrapBeforeAndAfter[T](op: => T): T = {
addWrapper()
try op finally addWrapper()
diff --git a/test/files/neg/t5148.check b/test/files/neg/t5148.check
index 25107c4dbe..f426bd2010 100644
--- a/test/files/neg/t5148.check
+++ b/test/files/neg/t5148.check
@@ -1,9 +1,5 @@
-error: bad symbolic reference. A signature in Imports.class refers to term global
+error: bad symbolic reference. A signature in Imports.class refers to type Request
in class scala.tools.nsc.interpreter.IMain which is not available.
It may be completely missing from the current classpath, or the version on
the classpath might be incompatible with the version used when compiling Imports.class.
-error: bad symbolic reference. A signature in Imports.class refers to term memberHandlers
-in class scala.tools.nsc.interpreter.IMain which is not available.
-It may be completely missing from the current classpath, or the version on
-the classpath might be incompatible with the version used when compiling Imports.class.
-two errors found
+one error found
diff --git a/test/files/run/t7747-repl.check b/test/files/run/t7747-repl.check
new file mode 100644
index 0000000000..ad924f482c
--- /dev/null
+++ b/test/files/run/t7747-repl.check
@@ -0,0 +1,286 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> var x = 10
+x: Int = 10
+
+scala> var y = 11
+y: Int = 11
+
+scala> x = 12
+x: Int = 12
+
+scala> y = 13
+y: Int = 13
+
+scala> val z = x * y
+z: Int = 156
+
+scala> 2 ; 3
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 2 ;;
+ ^
+res0: Int = 3
+
+scala> { 2 ; 3 }
+<console>:8: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ { 2 ; 3 }
+ ^
+res1: Int = 3
+
+scala> 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ 1 +
+ 2 +
+ 3 } ; bippy+88+11
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ ^
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ ^
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ ^
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ ^
+defined object Cow
+defined class Moo
+bippy: Int
+res2: Int = 105
+
+scala>
+
+scala> object Bovine { var x: List[_] = null } ; case class Ruminant(x: Int) ; bippy * bippy * bippy
+defined object Bovine
+defined class Ruminant
+res3: Int = 216
+
+scala> Bovine.x = List(Ruminant(5), Cow, new Moo)
+Bovine.x: List[Any] = List(Ruminant(5), Cow, Moooooo)
+
+scala> Bovine.x
+res4: List[Any] = List(Ruminant(5), Cow, Moooooo)
+
+scala>
+
+scala> (2)
+res5: Int = 2
+
+scala> (2 + 2)
+res6: Int = 4
+
+scala> ((2 + 2))
+res7: Int = 4
+
+scala> ((2 + 2))
+res8: Int = 4
+
+scala> ( (2 + 2))
+res9: Int = 4
+
+scala> ( (2 + 2 ) )
+res10: Int = 4
+
+scala> 5 ; ( (2 + 2 ) ) ; ((5))
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 5 ; ( (2 + 2 ) ) ;;
+ ^
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 5 ; ( (2 + 2 ) ) ;;
+ ^
+res11: Int = 5
+
+scala> (((2 + 2)), ((2 + 2)))
+res12: (Int, Int) = (4,4)
+
+scala> (((2 + 2)), ((2 + 2)), 2)
+res13: (Int, Int, Int) = (4,4,2)
+
+scala> (((((2 + 2)), ((2 + 2)), 2).productIterator ++ Iterator(3)).mkString)
+res14: String = 4423
+
+scala>
+
+scala> 55 ; ((2 + 2)) ; (1, 2, 3)
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 55 ; ((2 + 2)) ;;
+ ^
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 55 ; ((2 + 2)) ;;
+ ^
+res15: (Int, Int, Int) = (1,2,3)
+
+scala> 55 ; (x: Int) => x + 1 ; () => ((5))
+<console>:8: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 55 ; (x: Int) => x + 1 ;;
+ ^
+res16: () => Int = <function0>
+
+scala>
+
+scala> () => 5
+res17: () => Int = <function0>
+
+scala> 55 ; () => 5
+<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+ 55 ;;
+ ^
+res18: () => Int = <function0>
+
+scala> () => { class X ; new X }
+res19: () => AnyRef = <function0>
+
+scala>
+
+scala> def foo(x: Int)(y: Int)(z: Int) = x+y+z
+foo: (x: Int)(y: Int)(z: Int)Int
+
+scala> foo(5)(10)(15)+foo(5)(10)(15)
+res20: Int = 60
+
+scala>
+
+scala> List(1) ++ List('a')
+res21: List[AnyVal] = List(1, a)
+
+scala>
+
+scala> 1 to 100 map (_ + 1)
+res22: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101)
+
+scala> val x1 = 1
+x1: Int = 1
+
+scala> val x2 = 2
+x2: Int = 2
+
+scala> val x3 = 3
+x3: Int = 3
+
+scala> case class BippyBungus()
+defined class BippyBungus
+
+scala> x1 + x2 + x3
+res23: Int = 6
+
+scala> :reset
+Resetting interpreter state.
+Forgetting this session history:
+
+var x = 10
+var y = 11
+x = 12
+y = 13
+val z = x * y
+2 ; 3
+{ 2 ; 3 }
+5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ 1 +
+ 2 +
+ 3 } ; bippy+88+11
+object Bovine { var x: List[_] = null } ; case class Ruminant(x: Int) ; bippy * bippy * bippy
+Bovine.x = List(Ruminant(5), Cow, new Moo)
+Bovine.x
+(2)
+(2 + 2)
+((2 + 2))
+ ((2 + 2))
+ ( (2 + 2))
+ ( (2 + 2 ) )
+5 ; ( (2 + 2 ) ) ; ((5))
+(((2 + 2)), ((2 + 2)))
+(((2 + 2)), ((2 + 2)), 2)
+(((((2 + 2)), ((2 + 2)), 2).productIterator ++ Iterator(3)).mkString)
+55 ; ((2 + 2)) ; (1, 2, 3)
+55 ; (x: Int) => x + 1 ; () => ((5))
+() => 5
+55 ; () => 5
+() => { class X ; new X }
+def foo(x: Int)(y: Int)(z: Int) = x+y+z
+foo(5)(10)(15)+foo(5)(10)(15)
+List(1) ++ List('a')
+1 to 100 map (_ + 1)
+val x1 = 1
+val x2 = 2
+val x3 = 3
+case class BippyBungus()
+x1 + x2 + x3
+
+Forgetting all expression results and named terms: $intp, BippyBungus, Bovine, Cow, Ruminant, bippy, foo, x, x1, x2, x3, y, z
+Forgetting defined types: BippyBungus, Moo, Ruminant
+
+scala> x1 + x2 + x3
+<console>:8: error: not found: value x1
+ x1 + x2 + x3
+ ^
+<console>:8: error: not found: value x2
+ x1 + x2 + x3
+ ^
+<console>:8: error: not found: value x3
+ x1 + x2 + x3
+ ^
+
+scala> val x1 = 4
+x1: Int = 4
+
+scala> new BippyBungus
+<console>:8: error: not found: type BippyBungus
+ new BippyBungus
+ ^
+
+scala> class BippyBungus() { def f = 5 }
+defined class BippyBungus
+
+scala> { new BippyBungus ; x1 }
+res2: Int = 4
+
+scala> object x {class y { case object z } }
+defined object x
+
+scala> case class BippyBups()
+defined class BippyBups
+
+scala> case class PuppyPups()
+defined class PuppyPups
+
+scala> case class Bingo()
+defined class Bingo
+
+scala> List(BippyBups(), PuppyPups(), Bingo()) // show
+class $read extends Serializable {
+ def <init>() = {
+ super.<init>;
+ ()
+ };
+ class $iw extends Serializable {
+ def <init>() = {
+ super.<init>;
+ ()
+ };
+ import $line44.$read.$iw.$iw.BippyBups;
+ import $line44.$read.$iw.$iw.BippyBups;
+ import $line45.$read.$iw.$iw.PuppyPups;
+ import $line45.$read.$iw.$iw.PuppyPups;
+ import $line46.$read.$iw.$iw.Bingo;
+ import $line46.$read.$iw.$iw.Bingo;
+ class $iw extends Serializable {
+ def <init>() = {
+ super.<init>;
+ ()
+ };
+ val res3 = List(BippyBups, PuppyPups, Bingo)
+ };
+ val $iw = new $iw.<init>
+ };
+ val $iw = new $iw.<init>
+}
+object $read extends $read {
+ def <init>() = {
+ super.<init>;
+ ()
+ }
+}
+res3: List[Product with Serializable] = List(BippyBups(), PuppyPups(), Bingo())
+
+scala>
diff --git a/test/files/run/t7747-repl.scala b/test/files/run/t7747-repl.scala
new file mode 100644
index 0000000000..0e64210460
--- /dev/null
+++ b/test/files/run/t7747-repl.scala
@@ -0,0 +1,69 @@
+import scala.tools.partest.ReplTest
+import scala.tools.nsc.Settings
+
+object Test extends ReplTest {
+
+ override def transformSettings(s: Settings): Settings = {
+ s.Yreplclassbased.value = true
+ s
+ }
+
+ def code = """
+ |var x = 10
+ |var y = 11
+ |x = 12
+ |y = 13
+ |val z = x * y
+ |2 ; 3
+ |{ 2 ; 3 }
+ |5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ | 1 +
+ | 2 +
+ | 3 } ; bippy+88+11
+ |
+ |object Bovine { var x: List[_] = null } ; case class Ruminant(x: Int) ; bippy * bippy * bippy
+ |Bovine.x = List(Ruminant(5), Cow, new Moo)
+ |Bovine.x
+ |
+ |(2)
+ |(2 + 2)
+ |((2 + 2))
+ | ((2 + 2))
+ | ( (2 + 2))
+ | ( (2 + 2 ) )
+ |5 ; ( (2 + 2 ) ) ; ((5))
+ |(((2 + 2)), ((2 + 2)))
+ |(((2 + 2)), ((2 + 2)), 2)
+ |(((((2 + 2)), ((2 + 2)), 2).productIterator ++ Iterator(3)).mkString)
+ |
+ |55 ; ((2 + 2)) ; (1, 2, 3)
+ |55 ; (x: Int) => x + 1 ; () => ((5))
+ |
+ |() => 5
+ |55 ; () => 5
+ |() => { class X ; new X }
+ |
+ |def foo(x: Int)(y: Int)(z: Int) = x+y+z
+ |foo(5)(10)(15)+foo(5)(10)(15)
+ |
+ |List(1) ++ List('a')
+ |
+ |1 to 100 map (_ + 1)
+ |val x1 = 1
+ |val x2 = 2
+ |val x3 = 3
+ |case class BippyBungus()
+ |x1 + x2 + x3
+ |:reset
+ |x1 + x2 + x3
+ |val x1 = 4
+ |new BippyBungus
+ |class BippyBungus() { def f = 5 }
+ |{ new BippyBungus ; x1 }
+ |object x {class y { case object z } }
+ |case class BippyBups()
+ |case class PuppyPups()
+ |case class Bingo()
+ |List(BippyBups(), PuppyPups(), Bingo()) // show
+ |""".stripMargin
+}