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/ClassBasedPaths.scala8
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ILoop.scala5
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala52
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Imports.scala24
-rw-r--r--test/files/run/t7747-repl.check291
-rw-r--r--test/files/run/t7747-repl.scala69
7 files changed, 436 insertions, 14 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index b8ca4adc14..ac73df930a 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", "Uses classes instead of objects ( which is default) to wrap repl code.")
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/ClassBasedPaths.scala b/src/repl/scala/tools/nsc/interpreter/ClassBasedPaths.scala
new file mode 100644
index 0000000000..d242a6a0c0
--- /dev/null
+++ b/src/repl/scala/tools/nsc/interpreter/ClassBasedPaths.scala
@@ -0,0 +1,8 @@
+package scala.tools.nsc.interpreter
+
+trait ClassBasedPaths {
+ self: IMain =>
+ override def transformPath(p: String): String = p replaceFirst("read", "read.INSTANCE") replaceAll("iwC", "iw")
+
+ override def readRootPath(readPath: String) = getClassIfDefined(readPath)
+}
diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
index a96bed4696..4b90128a99 100644
--- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
@@ -119,8 +119,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
def createInterpreter() {
if (addedClasspath != "")
settings.classpath append addedClasspath
-
- intp = new ILoopInterpreter
+ if (settings.Yreplclassbased.value)
+ intp = new ILoopInterpreter with ClassBasedPaths
+ else intp = new ILoopInterpreter
}
/** print a friendly help message */
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index e4a3416152..b2d5a0a149 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -81,6 +81,8 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
private var _classLoader: util.AbstractFileClassLoader = null // active classloader
private val _compiler: ReplGlobal = newCompiler(settings, reporter) // our private compiler
+ val classBasedWrappers = settings.Yreplclassbased.value
+
def compilerClasspath: Seq[java.net.URL] = (
if (isInitializeComplete) global.classPath.asURLs
else new PathResolver(settings).result.asURLs // the compiler's classpath
@@ -272,12 +274,14 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
case s => s
} mkString "."
)
+ def transformPath(p: String): String = p
+ def readRootPath(readPath: String) = getModuleIfDefined(readPath)
abstract class PhaseDependentOps {
def shift[T](op: => T): T
def path(name: => Name): String = shift(path(symbolOfName(name)))
- def path(sym: Symbol): String = backticked(shift(sym.fullName))
+ def path(sym: Symbol): String = backticked(transformPath(shift(sym.fullName)))
def sig(sym: Symbol): String = shift(sym.defString)
}
object typerOp extends PhaseDependentOps {
@@ -700,7 +704,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 +781,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))
@@ -854,7 +858,29 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
/** the line of code to compute */
def toCompute = line
- def fullPath(vname: String) = s"${lineRep.readPath}$accessPath.`$vname`"
+ def fullPath(vname: String) = s"${codeWrapper.fullAccessPath}.`$vname`"
+
+ trait ClassBasedWrappers {
+ self: Wrappers =>
+ override def preambleHeader = "class %s extends Serializable {"
+
+ override def fullAccessPath = s"${lineRep.readPath}.INSTANCE$accessPath"
+
+ override def postamble = importsTrailer + "\n}" + s"""
+ |object ${lineRep.readName} {
+ |val INSTANCE = new ${lineRep.readName}();
+ |}""".stripMargin
+ }
+
+ class Wrappers {
+ def preambleHeader = "object %s {"
+
+ def fullAccessPath = s"${lineRep.readPath}$accessPath"
+
+ def postamble = importsTrailer + "\n}"
+ }
+
+ val codeWrapper = if (!classBasedWrappers) new Wrappers else new Wrappers with ClassBasedWrappers
/** generate the source code for the object that computes this request */
private object ObjectSourceCode extends IMain.CodeAssembler[MemberHandler] {
@@ -863,12 +889,14 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
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 {
+ val preamble = s"""
+ |${codeWrapper.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 postamble = codeWrapper.postamble
+
val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this
}
@@ -890,7 +918,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
| (""
""".stripMargin.format(
lineRep.evalName, evalResult, lineRep.printName,
- executionWrapper, lineRep.readName + accessPath
+ executionWrapper, codeWrapper.fullAccessPath
)
val postamble = """
@@ -1219,7 +1247,7 @@ object IMain {
// $line3.$read.$iw.$iw.Bippy =
// $line3.$read$$iw$$iw$Bippy@4a6a00ca
private def removeLineWrapper(s: String) = s.replaceAll("""\$line\d+[./]\$(read|eval|print)[$.]""", "")
- private def removeIWPackages(s: String) = s.replaceAll("""\$(iw|read|eval|print)[$.]""", "")
+ private def removeIWPackages(s: String) = s.replaceAll("""\$(iw|iwC|read|eval|print)[$.]""", "")
def stripString(s: String) = removeIWPackages(removeLineWrapper(s))
trait CodeAssembler[T] {
@@ -1239,6 +1267,7 @@ object IMain {
def stripImpl(str: String): String
def strip(str: String): String = if (isStripping) stripImpl(str) else str
}
+
trait TruncatingWriter {
def maxStringLength: Int
def isTruncating: Boolean
@@ -1248,6 +1277,7 @@ object IMain {
else str
}
}
+
abstract class StrippingTruncatingWriter(out: JPrintWriter)
extends JPrintWriter(out)
with StrippingWriter
diff --git a/src/repl/scala/tools/nsc/interpreter/Imports.scala b/src/repl/scala/tools/nsc/interpreter/Imports.scala
index ff7bfd432c..c8f9a677b2 100644
--- a/src/repl/scala/tools/nsc/interpreter/Imports.scala
+++ b/src/repl/scala/tools/nsc/interpreter/Imports.scala
@@ -130,13 +130,28 @@ trait Imports {
// add code for a new object to hold some imports
def addWrapper() {
+ if (classBasedWrappers) addClassBasedWrapper()
+ else addObjectBasedWrapper()
+ }
+
+ def addObjectBasedWrapper() {
val impname = nme.INTERPRETER_IMPORT_WRAPPER
code append "object %s {\n".format(impname)
trailingBraces append "}\n"
accessPath append ("." + impname)
currentImps.clear()
}
+
+ def addClassBasedWrapper() {
+ val impname = nme.INTERPRETER_IMPORT_WRAPPER
+ code append "class %sC extends Serializable {\n".format(impname)
+ trailingBraces append "}\nval " + impname + " = new " + impname + "C;\n"
+ accessPath append ("." + impname)
+ currentImps.clear()
+ }
+
def maybeWrap(names: Name*) = if (names exists currentImps) addWrapper()
+
def wrapBeforeAndAfter[T](op: => T): T = {
addWrapper()
try op finally addWrapper()
@@ -163,7 +178,14 @@ trait Imports {
case x =>
for (sym <- x.definedSymbols) {
maybeWrap(sym.name)
- code append s"import ${x.path}\n"
+ if (classBasedWrappers) {
+ if (!code.toString.endsWith(".`" + sym.name + "`;\n")) {
+ val valName = "$VAL" + req.lineRep.lineId
+ code.append("val " + valName + " = " + req.lineRep.readPath + ".INSTANCE;\n")
+ code.append("import " + valName + req.accessPath + ".`" + sym.name + "`;\n")
+ }
+ } else
+ code append s"import ${x.path}\n"
currentImps += sym.name
}
}
diff --git a/test/files/run/t7747-repl.check b/test/files/run/t7747-repl.check
new file mode 100644
index 0000000000..ae7ea67e61
--- /dev/null
+++ b/test/files/run/t7747-repl.check
@@ -0,0 +1,291 @@
+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)
+<console>:8: error: $VAL10 is already defined as value $VAL10
+val $VAL10 = INSTANCE;
+ ^
+<console>:12: error: $VAL11 is already defined as value $VAL11
+val $VAL11 = INSTANCE;
+ ^
+
+scala> Bovine.x
+res4: List[Any] = null
+
+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>:9: 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
+(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 $iwC extends Serializable {
+ def <init>() = {
+ super.<init>;
+ ()
+ };
+ val $VAL44 = $line44.$read.INSTANCE;
+ import $VAL44.$iw.$iw.BippyBups;
+ val $VAL45 = $line45.$read.INSTANCE;
+ import $VAL45.$iw.$iw.PuppyPups;
+ val $VAL46 = $line46.$read.INSTANCE;
+ import $VAL46.$iw.$iw.Bingo;
+ class $iwC extends Serializable {
+ def <init>() = {
+ super.<init>;
+ ()
+ };
+ val res3 = List(BippyBups, PuppyPups, Bingo)
+ };
+ val $iw = new $iwC.<init>
+ };
+ val $iw = new $iwC.<init>
+}
+object $read extends scala.AnyRef {
+ def <init>() = {
+ super.<init>;
+ ()
+ };
+ val INSTANCE = new $read.<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
+}